<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mariusz Szatkowski</title>
    <description>The latest articles on DEV Community by Mariusz Szatkowski (@wppolandcom).</description>
    <link>https://dev.to/wppolandcom</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2883374%2Ffb6ca62d-703d-417f-9e41-cffc9299a4a5.jpg</url>
      <title>DEV Community: Mariusz Szatkowski</title>
      <link>https://dev.to/wppolandcom</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wppolandcom"/>
    <language>en</language>
    <item>
      <title>Twelve months after migrating wppoland.com from WordPress to Astro on Cloudflare Pages</title>
      <dc:creator>Mariusz Szatkowski</dc:creator>
      <pubDate>Sun, 12 Jul 2026 16:04:21 +0000</pubDate>
      <link>https://dev.to/wppolandcom/twelve-months-after-migrating-wppolandcom-from-wordpress-to-astro-on-cloudflare-pages-11fi</link>
      <guid>https://dev.to/wppolandcom/twelve-months-after-migrating-wppolandcom-from-wordpress-to-astro-on-cloudflare-pages-11fi</guid>
      <description>&lt;p&gt;The replatform from WordPress to Astro was supposed to be the project. It turned out to be the prologue.&lt;/p&gt;

&lt;p&gt;Exporting content, rebuilding templates, getting a static site to compile and deploy to Cloudflare Pages took &lt;strong&gt;weeks&lt;/strong&gt;. Then the actual year began: redirects, hreflang, six-locale parity, and a build that outgrew the platform it was deployed to. This is where the time went on &lt;a href="https://wppoland.com/en/" rel="noopener noreferrer"&gt;wppoland.com&lt;/a&gt; - because it did not go where the planning said it would.&lt;/p&gt;

&lt;h2&gt;
  
  
  The port is the cheap part
&lt;/h2&gt;

&lt;p&gt;Everyone budgets for the visible migration, and the estimate is roughly right. Content out of WordPress, templates in Astro with Tailwind, &lt;code&gt;npm run build&lt;/code&gt;, deploy to Cloudflare Pages. A content site of moderate size reaches a working static build in weeks. This is the phase that demos well and convinces everyone the project is nearly done.&lt;/p&gt;

&lt;p&gt;It is not nearly done. A working build proves templates render. It proves nothing about whether old URLs resolve, whether locales agree with each other, or whether the build still finishes when content triples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Surprise 1: the redirect layer
&lt;/h2&gt;

&lt;p&gt;Every URL WordPress ever exposed and Google ever indexed needed a &lt;strong&gt;301&lt;/strong&gt; to its new Astro address. On a six-locale site with descriptive slugs that runs into &lt;strong&gt;thousands&lt;/strong&gt; of rules.&lt;/p&gt;

&lt;p&gt;Cloudflare Pages has a &lt;code&gt;_redirects&lt;/code&gt; file-size cap around &lt;strong&gt;100KB&lt;/strong&gt;. Past that, rules are &lt;strong&gt;silently dropped&lt;/strong&gt; - no build warning. We learned this months later from 404s in Search Console, long after the migration looked finished. The fix was a Functions middleware layer for overflow rules and explicit monitoring of redirect coverage.&lt;/p&gt;

&lt;p&gt;Lesson: the redirect map is not a checklist item. It is a system you operate, with its own failure modes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Surprise 2: six locales that must agree forever
&lt;/h2&gt;

&lt;p&gt;WordPress, with the right plugins, hides multilingual structure behind an admin. A static build exposes it.&lt;/p&gt;

&lt;p&gt;Six language versions of every article have to stay structurally parallel: same section headings in the same order, hreflang links that resolve to real siblings, canonical URLs that match intent, taxonomy URLs aligned across locales. When one locale drifts, the cross-language link graph cracks at the indexing layer while every page still renders fine.&lt;/p&gt;

&lt;p&gt;Post-migration, parity stopped being a milestone and became a &lt;strong&gt;standing check&lt;/strong&gt; on every content change. Six locales do not stay aligned on their own - especially when translation tooling touches structural fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  Surprise 3: the build outgrew Cloudflare's runner
&lt;/h2&gt;

&lt;p&gt;Cloudflare Pages &lt;strong&gt;serves&lt;/strong&gt; a large static site effortlessly. &lt;strong&gt;Building&lt;/strong&gt; one is bounded by memory. The platform build runner tops out around &lt;strong&gt;8GB heap&lt;/strong&gt;. Our multilingual Astro site with thousands of prerendered pages (locales multiply page count) exceeded that and failed with OOM errors that took time to recognize.&lt;/p&gt;

&lt;p&gt;The fix: stop building on the platform. Build locally with a &lt;strong&gt;16GB heap&lt;/strong&gt;, deploy the finished &lt;code&gt;dist/&lt;/code&gt; with &lt;strong&gt;Wrangler&lt;/strong&gt;. An M-series Mac finishes reliably in minutes where the constrained runner could not finish at all.&lt;/p&gt;

&lt;p&gt;Related discipline on images: Astro's asset pipeline optimizes at build time (excellent, memory-hungry). Pre-optimized hero backgrounds live in &lt;code&gt;public/&lt;/code&gt; served as-is; only images that genuinely benefit from pipeline processing stay in the asset folder, kept reasonably sized.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you rebuild that WordPress gave you free
&lt;/h2&gt;

&lt;p&gt;Leaving WordPress means inheriting checks plugins used to perform invisibly: internal link validation, sitemap freshness, broken reference warnings in the admin.&lt;/p&gt;

&lt;p&gt;Over the year we wired validators into deploy: internal-link scans against the real route surface, structured-data shape checks across locales, parity checks comparing language siblings for missing sections, build-time sitemap and hreflang generation. Each replaces something WordPress did silently. More control, more confidence - but real engineering the word &lt;strong&gt;migration&lt;/strong&gt; does not mention.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the move actually bought
&lt;/h2&gt;

&lt;p&gt;Stated plainly so the year does not read as regret: &lt;strong&gt;worth it&lt;/strong&gt; for this shape of site.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pages render from the edge as static files - faster and steadier than request-time PHP&lt;/li&gt;
&lt;li&gt;Attack surface shrank to roughly nothing dynamic&lt;/li&gt;
&lt;li&gt;Crawler access became predictable (important when AI assistants fetch HTML without running your JavaScript)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The honest qualifier: right trade for a &lt;strong&gt;content site&lt;/strong&gt;; wrong trade for a site that lives on dynamic, logged-in functionality where WordPress's ecosystem is the feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR for anyone scoping a similar move
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;What hurts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Port (templates + content)&lt;/td&gt;
&lt;td&gt;Weeks&lt;/td&gt;
&lt;td&gt;Underestimated but manageable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redirect map + monitoring&lt;/td&gt;
&lt;td&gt;Months&lt;/td&gt;
&lt;td&gt;Silent caps, Search Console lag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Locale parity&lt;/td&gt;
&lt;td&gt;Continuous&lt;/td&gt;
&lt;td&gt;Drift is invisible until indexing breaks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build scaling&lt;/td&gt;
&lt;td&gt;One hard week&lt;/td&gt;
&lt;td&gt;OOM on platform runner; local build + Wrangler&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Budget for the &lt;strong&gt;tail&lt;/strong&gt;, not the port.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;We run &lt;a href="https://wppoland.com/en/" rel="noopener noreferrer"&gt;WPPoland&lt;/a&gt; - senior WordPress and Astro engineering from Gdynia, Poland. If you are mid-migration and the redirect map or six-locale parity is where the project stalled, that is usually the work we get called for.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related on DEV:&lt;/strong&gt; &lt;a href="https://dev.to/wppolandcom/a-read-only-mcp-server-for-woocommerce-what-ai-agents-actually-need-from-a-store-3fk6"&gt;Read-only MCP server for WooCommerce&lt;/a&gt; · &lt;a href="https://dev.to/wppolandcom/syncing-a-wholesalers-api-into-woocommerce-without-overselling-or-melting-the-server-38ao"&gt;Wholesaler API sync into WooCommerce&lt;/a&gt;&lt;/p&gt;

</description>
      <category>astro</category>
      <category>wordpress</category>
      <category>cloudflare</category>
      <category>jamstack</category>
    </item>
    <item>
      <title>Syncing a wholesaler's API into WooCommerce without overselling or melting the server</title>
      <dc:creator>Mariusz Szatkowski</dc:creator>
      <pubDate>Tue, 07 Jul 2026 09:43:56 +0000</pubDate>
      <link>https://dev.to/wppolandcom/syncing-a-wholesalers-api-into-woocommerce-without-overselling-or-melting-the-server-38ao</link>
      <guid>https://dev.to/wppolandcom/syncing-a-wholesalers-api-into-woocommerce-without-overselling-or-melting-the-server-38ao</guid>
      <description>&lt;p&gt;A common WooCommerce brief looks like this: the store does not own its inventory. A distributor does. The shop is a storefront on top of a wholesaler whose catalog, stock levels, and prices change daily, exposed through some REST or XML web service. The job is to make the store reflect the supplier's reality automatically, and to never sell something the supplier cannot ship.&lt;/p&gt;

&lt;p&gt;We shipped exactly this for an automotive-parts store recently (client and supplier stay anonymous). Tens of thousands of indexes, a wholesaler REST API, and a hard requirement: no manual catalog work, and no orders for parts that are not actually in stock. Here is what the architecture looks like and the traps worth knowing before you start.&lt;/p&gt;

&lt;h2&gt;
  
  
  The store is a view, the wholesaler is the source of truth
&lt;/h2&gt;

&lt;p&gt;The first mental shift is that WooCommerce is not the system of record for products. The distributor is. WooCommerce is a cache with a checkout attached. Once you accept that, the design falls out: a sync layer pulls from the supplier and writes into WooCommerce on a schedule, and you treat the WooCommerce product data as derived, not authored.&lt;/p&gt;

&lt;p&gt;The integration answers three questions, and you should answer them explicitly before writing code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What syncs&lt;/strong&gt; - catalog, attributes, media, stock, price.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Which direction&lt;/strong&gt; - here it is one-way (supplier to store); orders stay in WooCommerce.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How often&lt;/strong&gt; - split it. Stock and price are cheap and change constantly, so poll them frequently. Full catalog and media are expensive, so refresh them rarely.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Map fields declaratively, or you will rewrite it every month
&lt;/h2&gt;

&lt;p&gt;The supplier describes a product its way (its own index, EAN, attribute names, HTML description blobs, image URLs). WooCommerce wants its way (product, attributes, variations, media library). The bridge between them is a field map, and the single best decision we made was keeping that map declarative - a data structure, not a pile of &lt;code&gt;if&lt;/code&gt; statements. When the wholesaler adds a new attribute, you extend the map; you do not touch the sync logic. EAN and the supplier index become the stable keys that let each cycle find "the same product" again instead of creating duplicates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stock protection is the whole point, so make it boring
&lt;/h2&gt;

&lt;p&gt;The expensive failure mode is selling a part the supplier does not have. So the stock rule is deliberately dumb: every cycle, if the supplier says an index is unavailable, the product is hidden or set to out-of-stock in WooCommerce. When availability returns, so does the product. No cleverness, no "probably still has some." The customer literally cannot add an unfulfillable item to the cart, because it is not purchasable when the supplier is dry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Price is cost plus rules, computed at write time
&lt;/h2&gt;

&lt;p&gt;Wholesaler prices are cost, not what you sell at. Above the pull layer sits margin logic: the supplier's net price comes in, the store's margin rule is applied, and only the result is written to WooCommerce as the product price. The owner steers profitability by editing rules, not by touching thousands of prices by hand. When the supplier's price list moves, the next cycle recomputes and the store stays profitable without anyone noticing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part everyone underestimates: not melting the server
&lt;/h2&gt;

&lt;p&gt;Tens of thousands of products times "poll often" is how you take down a shared-hosting WooCommerce site. A few things that mattered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Do not do it in &lt;code&gt;WP-Cron&lt;/code&gt; on page loads.&lt;/strong&gt; Drive the sync from real scheduled tasks (system cron hitting WP-CLI, or Action Scheduler) so a burst of traffic does not trigger a burst of syncs, and a sync does not block a customer's request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch and paginate.&lt;/strong&gt; Pull and write in chunks; never load the whole catalog into memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split the cadences.&lt;/strong&gt; Stock/price on a short interval, full catalog/media on a long one. Most of your cycles should be small and cheap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be idempotent.&lt;/strong&gt; A cycle that dies halfway must be safe to re-run. Keying on EAN/index makes that free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch autoload and &lt;code&gt;wp_postmeta&lt;/code&gt;.&lt;/strong&gt; Large catalogs bloat both; a sync that writes sloppily to postmeta will quietly wreck query performance long before anyone blames the sync.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When this pattern generalizes
&lt;/h2&gt;

&lt;p&gt;The interesting bit is that "wholesaler REST API" is not special. Swap the source for an ERP (Comarch, Dynamics, NetSuite, whatever exposes an API) and the shape is identical: pull, map, protect stock, compute price, schedule sanely, stay idempotent. The data source changes; the discipline does not. That is why we treat wholesaler-feed and ERP integrations as the same class of problem.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written by &lt;a href="https://wppoland.com/en/" rel="noopener noreferrer"&gt;WPPoland&lt;/a&gt;. We build &lt;a href="https://wppoland.com/en/woocommerce-erp-integration/" rel="noopener noreferrer"&gt;WooCommerce ERP and wholesale-API integrations&lt;/a&gt; for stores that outgrew manual catalog work. Also on GitHub: a read-only &lt;a href="https://github.com/wppoland/woocommerce-mcp" rel="noopener noreferrer"&gt;WooCommerce MCP server&lt;/a&gt; for giving AI agents safe read access to a store.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related on DEV:&lt;/strong&gt; &lt;a href="https://dev.to/wppolandcom/a-read-only-mcp-server-for-woocommerce-what-ai-agents-actually-need-from-a-store-3fk6"&gt;Read-only MCP server for WooCommerce&lt;/a&gt; · &lt;a href="https://dev.to/wppolandcom/twelve-months-after-migrating-wppolandcom-from-wordpress-to-astro-on-cloudflare-pages-11fi"&gt;Twelve months migrating WordPress to Astro&lt;/a&gt;&lt;/p&gt;

</description>
      <category>woocommerce</category>
      <category>wordpress</category>
      <category>php</category>
      <category>api</category>
    </item>
    <item>
      <title>A read-only MCP server for WooCommerce: what AI agents actually need from a store</title>
      <dc:creator>Mariusz Szatkowski</dc:creator>
      <pubDate>Tue, 07 Jul 2026 09:38:47 +0000</pubDate>
      <link>https://dev.to/wppolandcom/a-read-only-mcp-server-for-woocommerce-what-ai-agents-actually-need-from-a-store-3fk6</link>
      <guid>https://dev.to/wppolandcom/a-read-only-mcp-server-for-woocommerce-what-ai-agents-actually-need-from-a-store-3fk6</guid>
      <description>&lt;p&gt;Every WooCommerce integration project we take on now ends with the same question from the client: "can the AI just check the store for me?" What were last month's sales, which SKUs are out of stock, has order 48821 shipped. Reasonable questions. The tempting answer is to hand an agent full API access and let it figure things out. That is also how you end up with an LLM editing prices at 2am because it "helpfully" corrected what it thought was a typo.&lt;/p&gt;

&lt;p&gt;So we built the boring version instead: &lt;a href="https://github.com/wppoland/woocommerce-mcp" rel="noopener noreferrer"&gt;&lt;code&gt;woocommerce-mcp&lt;/code&gt;&lt;/a&gt;, a small &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;Model Context Protocol&lt;/a&gt; server that gives Claude (or any MCP client) read access to a live WordPress + WooCommerce store, and nothing else. It is open source (MIT) and this post is about the design decisions, because the interesting part is not the code, it is what we deliberately left out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read-only is a feature, not a limitation
&lt;/h2&gt;

&lt;p&gt;The single most important line in the whole project is that there are no write tools. Not "writes behind a confirmation," not "writes gated by a flag." None. The server exposes five tools and all five only read:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it answers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_products&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;search products by name/sku, with price, stock, permalink&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_product&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;full detail for one product&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list_orders&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;recent orders, newest first, optional status filter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sales_report&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;totals for a period (week / month / last_month / year)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;search_posts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;published blog posts (public WP REST API, no keys)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The reasoning is blast radius. An agent that can only read cannot oversell inventory, cannot mangle a price, cannot cancel an order because it misread a prompt. When something inevitably goes sideways in the model's reasoning, the worst outcome is a wrong answer, not a wrong database. For a store owner deciding whether to point an AI at their production shop, "it physically cannot change anything" is the sentence that closes the conversation. You can always add a separate, explicitly-scoped write path later, with human confirmation, as its own project. Bundling it into the read tool by default is how you lose trust on day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope the keys, not just the code
&lt;/h2&gt;

&lt;p&gt;Read-only tools are only half the guarantee. The other half is the credential. WooCommerce REST keys come in Read, Write, and Read/Write. The README is blunt about it: create the key with &lt;strong&gt;Read&lt;/strong&gt; permission only. The server sends it to your own store over HTTPS as query auth. Even if a future bug or a prompt-injected tool call tried to POST, the key itself would be refused by WooCommerce. Two independent layers (no write tools, no write key) both have to fail for anything to change. That is the level of paranoia a production store deserves.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;search_posts&lt;/code&gt; needs no keys at all - it hits the public WP REST API - so you can demo the thing against any WordPress site before you ever generate a credential.&lt;/p&gt;

&lt;h2&gt;
  
  
  No plugin, no lock-in
&lt;/h2&gt;

&lt;p&gt;There is nothing to install on the store. The server talks to the REST endpoints WooCommerce already ships. That matters for two reasons: you are not asking a client to add PHP to their production site just to try an AI experiment, and you are not on the hook for a plugin's update treadmill. The MCP server runs wherever the agent runs (locally over stdio, or in a container - there is a Dockerfile), and the store stays untouched.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack is deliberately thin
&lt;/h2&gt;

&lt;p&gt;TypeScript, the official &lt;code&gt;@modelcontextprotocol/sdk&lt;/code&gt;, &lt;code&gt;zod&lt;/code&gt; for tool input schemas, and that is the dependency list. stdio transport, so it drops straight into Claude Desktop, Cursor, or any MCP client config without a hosted endpoint. The whole thing is a few hundred lines. MCP rewards small, single-purpose servers: one that does WooCommerce reads well composes better with other tools than a monolith that tries to own the entire store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ops guide (July 2026)
&lt;/h2&gt;

&lt;p&gt;We also published a longer install and security write-up for the shipped package on npm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Guide: &lt;a href="https://wppoland.com/en/woocommerce-mcp-open-source-read-only/" rel="noopener noreferrer"&gt;https://wppoland.com/en/woocommerce-mcp-open-source-read-only/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/@wppoland/woocommerce-mcp" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@wppoland/woocommerce-mcp&lt;/a&gt; (&lt;code&gt;@wppoland/woocommerce-mcp@0.1.1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;MCP Registry: &lt;code&gt;io.github.wppoland/woocommerce-mcp&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where this goes next
&lt;/h2&gt;

&lt;p&gt;The read-only server is the safe entry point. In real projects it is usually step one of a bigger picture: syncing that same catalog and stock data the other direction, into an ERP or a wholesaler feed, where the writes are careful, queued, and reconciled. That is a different risk profile and a different piece of software. Keeping the read layer separate and trivially auditable is what makes the rest of the integration safe to reason about.&lt;/p&gt;

&lt;p&gt;If you run a WooCommerce store and want to try it, the repo has the two-minute setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/wppoland/woocommerce-mcp.git
&lt;span class="nb"&gt;cd &lt;/span&gt;woocommerce-mcp &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then point your MCP client at &lt;code&gt;dist/index.js&lt;/code&gt; with &lt;code&gt;WP_URL&lt;/code&gt; and your Read-only WooCommerce keys.&lt;/p&gt;

&lt;p&gt;Code, issues, and the full tool reference: &lt;strong&gt;&lt;a href="https://github.com/wppoland/woocommerce-mcp" rel="noopener noreferrer"&gt;https://github.com/wppoland/woocommerce-mcp&lt;/a&gt;&lt;/strong&gt;. Feedback welcome, especially on which read-only tools you would want next - refunds summary, customer lookup, coupon status are the three we hear most.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built by the team at &lt;a href="https://wppoland.com/en/" rel="noopener noreferrer"&gt;WPPoland&lt;/a&gt;, where we spend most of our time on the less glamorous half of AI commerce: &lt;a href="https://wppoland.com/en/woocommerce-erp-integration/" rel="noopener noreferrer"&gt;wiring WooCommerce into ERPs and wholesaler APIs&lt;/a&gt; so stock and prices stay honest.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Related on DEV:&lt;/strong&gt; &lt;a href="https://dev.to/wppolandcom/syncing-a-wholesalers-api-into-woocommerce-without-overselling-or-melting-the-server-38ao"&gt;Wholesaler API sync into WooCommerce&lt;/a&gt; · &lt;a href="https://dev.to/wppolandcom/twelve-months-after-migrating-wppolandcom-from-wordpress-to-astro-on-cloudflare-pages-11fi"&gt;Twelve months migrating WordPress to Astro&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>woocommerce</category>
      <category>wordpress</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
