Forem

Cover image for Why your WooCommerce AI Agent endpoint is slow (and how we fixed it in 30 lines)
Almin Zolotic
Almin Zolotic

Posted on

Why your WooCommerce AI Agent endpoint is slow (and how we fixed it in 30 lines)

Published by Almin Zolotic — Zologic / UCPReady


We spent most of yesterday debugging an 8-second ucp_list_products response on a live WooCommerce store with 40,000+ SKUs. The fix was 30 lines of PHP. Here's the full investigation — every dead end included — because anyone building on WooCommerce + UCP is going to hit this.


The setup

houseofparfum.nl runs UCPReady — our WooCommerce plugin that implements the Universal Commerce Protocol (UCP) over REST, MCP, and Embedded Checkout. It's been running in production since February, and UCP Playground benchmarks consistently showed ucp_list_products taking 8,432ms.

That's not a typo. Eight seconds. On every cold session.

For context: Shopify's median MCP tool call latency is 146ms. An AI agent making 4-5 tool calls in a session at 8 seconds each is going to hit platform timeouts. Sessions fail. Purchases don't complete.

We needed to find and fix the root cause.


Eliminating suspects

Suspect 1: The database query

First thing to check — is the WooCommerce product query slow?

wp eval '
$start = microtime(true);
$results = wc_get_products([
    "status" => "publish",
    "limit" => 10,
    "paginate" => true,
]);
echo "Query: " . round((microtime(true) - $start) * 1000) . "ms\n";
echo "Total: " . $results->total . "\n";
' --allow-root
Enter fullscreen mode Exit fullscreen mode

Result: 159ms for 38,433 products. Fast. Not the problem.

Suspect 2: Product transformation

UCPReady transforms WC_Product objects into UCP format on each request. How long does that take for 10 products?

wp eval '
$adapter = UCPReady\Adapters\ProductAdapter::get_instance();
$results = wc_get_products(["status" => "publish", "limit" => 10]);
$start = microtime(true);
foreach ($results as $product) {
    $adapter->to_ucp_list_format($product);
}
echo "Transform: " . round((microtime(true) - $start) * 1000) . "ms\n";
' --allow-root
Enter fullscreen mode Exit fullscreen mode

Result: 10ms. Not the problem.

Suspect 3: Redis / object cache

The store has Redis installed. Checking if it's actually connected to WordPress:

wp cache type --allow-root
# Redis ✓

wp eval '
$found = false;
wp_cache_get("ucp_product_list_82192", "ucpready", false, $found);
echo $found ? "CACHED" : "NOT CACHED";
' --allow-root
# NOT CACHED
Enter fullscreen mode Exit fullscreen mode

Redis is connected but product list cache keys aren't being populated. Separate issue — but not the 8-second culprit either.

Suspect 4: The taxonomy mapper

UCPReady maps WooCommerce categories to Google Product Taxonomy IDs. This involves loading a 482KB taxonomy file and building an inverted index. Was it rebuilding on every request?

wp eval '
$start = microtime(true);
$mapper = UCPReady\Taxonomy\TaxonomyMapper::get_instance();
$mapper->map_category(17, "en_US");
echo round((microtime(true) - $start) * 1000) . "ms\n";
' --allow-root
# 18ms
Enter fullscreen mode Exit fullscreen mode

18ms cold. Fast. And we fixed the caching here anyway — the built inverted index now persists to Redis with a 6-hour TTL. But still not the 8-second culprit.

The real test: HTTP vs WP-CLI

At this point we noticed something odd. Every WP-CLI test was fast. The same operations over HTTP were taking 8 seconds. WP-CLI has WordPress already loaded in memory. HTTP requests boot WordPress from scratch on every call.

time curl -s -o /dev/null -X POST \
  "https://houseofparfum.nl/wp-json/ucpready/v1/mcp" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"ucp_list_products","arguments":{"limit":5}}}'

# real 0m8.101s
Enter fullscreen mode Exit fullscreen mode

8 seconds over HTTP. 169ms in WP-CLI. The gap is WordPress boot time.


Finding the culprit

The store has 17 active plugins. Something in that list was making every MCP request boot WordPress for 8 seconds. We tested methodically:

wp plugin deactivate google-listings-and-ads --allow-root

time curl -s -o /dev/null -X POST \
  "https://houseofparfum.nl/wp-json/ucpready/v1/mcp" \
  ...

# real 0m0.651s
Enter fullscreen mode Exit fullscreen mode

Google Listings and Ads was adding 7.5 seconds to every single MCP request.

Not because of the product query. Not because of anything UCP-related. The plugin registers expensive hooks during WordPress initialisation that fire on every request — including REST API requests that have nothing to do with Google Shopping feeds.

This isn't a UCPReady problem or even a UCP problem. It's a WooCommerce plugin ecosystem problem. Any plugin that does expensive work on init or similar hooks will slow down every MCP request on every WooCommerce store running it.


The fix: Partial Bootstrap Mode

The solution is a must-use plugin that fires before regular plugins load and removes the offenders from the active plugins list for MCP requests only. Everything else on the store — admin, storefront, Google Shopping feed generation — is completely untouched.

<?php
/**
 * UCPReady MCP Partial Bootstrap
 * Place in /wp-content/mu-plugins/ucpready-mcp-bootstrap.php
 */

if ( ! defined( 'ABSPATH' ) ) { exit; }

$ucpready_skip = array(
    'google-listings-and-ads/google-listings-and-ads.php',
    'query-monitor/query-monitor.php',
    'ajax-search-for-woocommerce/ajax-search-for-woocommerce.php',
);

$ucpready_uri    = $_SERVER['REQUEST_URI'] ?? '';
$ucpready_is_mcp = strpos( $ucpready_uri, '/ucpready/v1/mcp' ) !== false
    || strpos( $ucpready_uri, 'rest_route=/ucpready/v1/mcp' ) !== false;

if ( $ucpready_is_mcp ) {
    add_filter( 'option_active_plugins', function( $plugins ) use ( $ucpready_skip ) {
        if ( ! is_array( $plugins ) ) return $plugins;
        return array_values( array_diff( $plugins, $ucpready_skip ) );
    }, 1 );
}
Enter fullscreen mode Exit fullscreen mode

That's the entire fix. The option_active_plugins filter with priority 1 fires before any plugin's code runs, so the offending plugins never register their hooks at all.


Results

time curl -s -o /dev/null -X POST \
  "https://houseofparfum.nl/wp-json/ucpready/v1/mcp" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"ucp_list_products","arguments":{"limit":5}}}'

# real 0m0.272s
Enter fullscreen mode Exit fullscreen mode

Then we ran a full timing breakdown:

curl -w "DNS:%{time_namelookup} Connect:%{time_connect} SSL:%{time_appconnect} TTFB:%{time_starttransfer} Total:%{time_total}\n" ...

# DNS:0.001 Connect:0.001 SSL:0.038 TTFB:0.038 Total:0.267
Enter fullscreen mode Exit fullscreen mode

Server TTFB: 38ms. The remaining ~4.5 seconds in Playground benchmarks is geographic network latency from US/UK servers to the Netherlands — not fixable at the server level.

WooCommerce at 38ms TTFB matches Shopify's performance characteristics. The variable isn't the platform. It's plugin configuration.


What this means for the WooCommerce + UCP ecosystem

This finding applies to every WooCommerce UCP implementation, not just UCPReady. If you're building on WooCommerce + UCP and ucp_list_products is slow, run this diagnostic:

# 1. Baseline with all plugins
time curl -s -o /dev/null -X POST "https://yourstore.com/wp-json/ucpready/v1/mcp" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"ucp_list_products","arguments":{"limit":5}}}'

# 2. Deactivate plugins one by one and retest
wp plugin deactivate <plugin-name> --allow-root
# retest
wp plugin activate <plugin-name> --allow-root

# 3. Once you find the culprit, add it to the mu-plugin skip list
Enter fullscreen mode Exit fullscreen mode

Common suspects beyond Google Listings: any plugin that runs expensive init hooks, external API calls during boot, or complex query modifications that fire on every request.


The broader lesson

AI agents make rapid sequential tool calls. A human tolerates a 3-second page load. An agent making 5 tool calls at 3 seconds each blows through platform timeouts and fails the session. Performance that was "acceptable" for human traffic becomes a hard blocker for agent traffic.

WooCommerce's plugin ecosystem is powerful but it was built for humans, not agents. Every plugin that does expensive boot-time work is a potential agent-killer. The mu-plugin pattern above is the pragmatic fix until the ecosystem catches up.


What we're working on next

The mu-plugin currently requires manual installation. We're adding a performance dashboard to UCPReady's admin settings that measures actual MCP response times, identifies problematic plugins automatically, and provides the mu-plugin as a one-click download when response times are above threshold.

We're also exploring whether it makes sense to report this to the Google Listings and Ads team directly. The fix on their side is a single is_rest_api_request() guard before registering expensive hooks. One line on their end helps every WooCommerce store, not just UCPReady users.


UCPReady is a WooCommerce plugin that implements the Universal Commerce Protocol — turning any WooCommerce store into a fully agent-executable commerce endpoint. Live demo: houseofparfum.nl | Plugin: zologic.nl

If your WooCommerce store is slow for AI agent traffic, I offer agent readiness audits. Reach out at contact@zologic.nl

Top comments (0)