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
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
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
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
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
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
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 );
}
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
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