{"id":32123,"date":"2026-04-02T14:51:59","date_gmt":"2026-04-02T19:51:59","guid":{"rendered":"https:\/\/wpengine.com\/builders\/?p=32123"},"modified":"2026-04-24T10:34:40","modified_gmt":"2026-04-24T15:34:40","slug":"mcp-wordpress-cloudinary-smart-search","status":"publish","type":"post","link":"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/","title":{"rendered":"Using MCP Servers in Concert: Smart Search AI, WordPress\u00ae, and Cloudinary Integration"},"content":{"rendered":"\n<p><br>When building AI applications, you often face a fragmented data problem. Images live in Cloudinary, blog posts exist in WordPress\u00ae\u00b9, and each system has its own search. Users can&#8217;t ask &#8220;show me travel content&#8221; and get both images and posts\u2014they have to search each system separately. This creates a poor experience and limits the possibilities of what can be built.<\/p>\n\n\n\n<p>The Smart Search AI MCP solves this by acting as a single search layer across all of your content. It indexes data from multiple sources, including Cloudinary assets, WordPress posts, and anything else you manage. This ensures natural language queries return results no matter where the content lives. Instead of building separate search interfaces for each service, you build one chatbot that understands everything.<\/p>\n\n\n\n<p>This guide extends the previous Smart Search AI MCP tutorial. The difference here is we&#8217;ll orchestrate three MCP servers, Smart Search AI, WordPress, and Cloudinary, so that natural language prompts can discover content, create posts with images, and automatically index everything for future searches.<br><\/p>\n\n\n\n<p>By the end of this article, you&#8217;ll have a chatbot that can do the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Search Cloudinary for images using natural language.<\/li>\n\n\n\n<li>Create WordPress posts with embedded Cloudinary images.<\/li>\n\n\n\n<li>Automatically index both assets and posts into Smart Search AI.<\/li>\n\n\n\n<li>Retrieve results across all content sources through conversation.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p>The result is a RAG (Retrieval-Augmented Generation) system where content creation and discovery happen through one interface.<\/p>\n\n\n\n<p>If you prefer the video versions of an article, here is the video format for this one:<\/p>\n\n\n\n<iframe class=\"youtube-video\"  src=\"https:\/\/www.youtube.com\/embed\/HzU8HOn1op4\" title=\"Using MCP Servers in Concert: Smart Search AI, WordPress\u00ae, and Cloudinary Integration\" frameborder=\"0\" style=\"display: block; margin: auto; width: 100%; height: 400px;\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen><\/iframe>\n\n\n\n<p><br><\/p>\n\n\n\n<div class=\"wp-block-group has-polar-background-color has-background is-layout-flow wp-container-core-group-is-layout-7a03825d wp-block-group-is-layout-flow\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--40);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--40)\">\n<p class=\"has-large-font-size\"><strong>Table of Contents<\/strong><\/p>\n\n\n\n<ul id=\"Prerequisites\" class=\"wp-block-list\">\n<li id=\"Prerequisites\"><a href=\"#prerequisites\">Prerequisites<\/a><\/li>\n\n\n\n<li><a href=\"#understanding-mcp-architecture\">Understanding the MCP Architecture <\/a><\/li>\n\n\n\n<li id=\"steps-for-setting-up\"><a href=\"#smart-searchai-indexing-loop\">The Smart Search AI Indexing Loop<\/a><\/li>\n\n\n\n<li><a href=\"#setting-up-wordpress-mcp-plugin\">Setting Up the WordPress MCP Server Plugin<\/a><\/li>\n\n\n\n<li><a href=\"#refactoring-nextjs-route-handler\">Refactoring the Next.js Route Handler<\/a><\/li>\n\n\n\n<li><a href=\"#connecting-with-asana\" type=\"internal\" id=\"#connecting-with-asana\">Testing Multi-MCP Setup<\/a><\/li>\n\n\n\n<li><a href=\"#conclusion\">Conclusion<\/a><\/li>\n<\/ul>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"prerequisites\">Prerequisites<\/h2>\n\n\n\n<p>&#8211; The basics from our previous <a href=\"https:\/\/wpengine.com\/builders\/wp-engine-smart-search-mcp-in-headless-wp\/\">Smart Search RAG chatbot guide&nbsp;<\/a><\/p>\n\n\n\n<p>&#8211; WordPress plugin development fundamentals<\/p>\n\n\n\n<p>&#8211; <a href=\"https:\/\/nextjs.org\/docs\/pages\/building-your-application\/routing\/api-routes\">Next.js API routes<\/a> and <a href=\"https:\/\/nextjs.org\/docs\/app\/api-reference\/edge\">Edge runtime<\/a><\/p>\n\n\n\n<p>&#8211; <a href=\"https:\/\/modelcontextprotocol.io\/\">Model Context Protocol<\/a> (MCP) concepts<\/p>\n\n\n\n<p>You&#8217;ll also need:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A WordPress site with WP Engine Smart Search AI configured with MCP Endpoint<\/li>\n\n\n\n<li><a href=\"https:\/\/cloudinary.com\/users\/register_free\">Cloudinary account <\/a>with API credentials<\/li>\n\n\n\n<li>The completed Smart Search RAG chatbot code from the previous tutorial<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"understanding-mcp-architecture\">Understanding the MCP Architecture<\/h2>\n\n\n\n<p>Model Context Protocol (MCP) provides a standardized way for AI models to discover and interact with external tools and data sources. In this extended implementation, we&#8217;re orchestrating three MCP servers that work in concert:<\/p>\n\n\n\n<p>1. Smart Search MCP &#8211; Semantic search and content retrieval across your indexed data<\/p>\n\n\n\n<p>2. Cloudinary MCP &#8211; Asset management for searching, listing, and transforming media<\/p>\n\n\n\n<p>3. WordPress MCP &#8211; Post creation, updates, and sending indexing requests to Smart Search AI<\/p>\n\n\n\n<p>The architecture looks like this:<br><\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>Next.js Chatbot (route.ts)\n<\/span><\/span><span class='shcb-loc'><span>    \u2193\n<\/span><\/span><span class='shcb-loc'><span>    \u251c\u2500\u2192 Smart Search MCP (search\/fetch content)\n<\/span><\/span><span class='shcb-loc'><span>    \u251c\u2500\u2192 Cloudinary MCP (find\/manage images)\n<\/span><\/span><span class='shcb-loc'><span>    \u2514\u2500\u2192 WordPress MCP (create posts, create posts, send data to Smart Search AI)\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><\/code><\/span><\/pre>\n\n\n<p>What makes this valuable is that the AI can orchestrate complex workflows across all three services. For example, a single prompt like <em>&#8220;Find Return of the Jedi images in Cloudinary and create a WordPress post with the best one&#8221;<\/em> triggers multiple MCP tool calls in sequence, with each service contributing its specialized functionality.<br><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"smart-searchai-indexing-loop\">The Smart Search AI Indexing Loop<\/h2>\n\n\n\n<p>Smart Search AI becomes your unified search layer that makes both Cloudinary assets and WordPress posts discoverable through natural language queries.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How Data Flows Into Smart Search AI<\/h3>\n\n\n\n<p><\/p>\n\n\n\n<p><strong><em>Cloudinary Assets \u2192 Smart Search AI:<\/em><\/strong><br>When you upload images to Cloudinary (via Cloudinary MCP tools), they exist in Cloudinary&#8217;s system but aren&#8217;t searchable via Smart Search AI yet. To make them searchable, the WordPress MCP plugin sends Cloudinary metadata to Smart Search AI through GraphQL mutations. Smart Search AI receives this data and indexes it, making the assets discoverable through natural language queries.<\/p>\n\n\n\n<p>The metadata sent includes the <code>`public_id`<\/code>, secure URL, resource type, format, and tags. Smart Search AI indexes all of these as searchable fields.<\/p>\n\n\n\n<p>Documents are indexed with an <code>cloudinary: prefix <\/code>for identification:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>```\n<\/span><\/span><span class='shcb-loc'><span>Document ID: cloudinary:sunset_beach_abc123\n<\/span><\/span><span class='shcb-loc'><span>Searchable fields:\n<\/span><\/span><span class='shcb-loc'><span>  - cloudinary_public_id: <span class=\"hljs-string\">\"sunset_beach_abc123\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  - cloudinary_url: <span class=\"hljs-string\">\"https:\/\/res.cloudinary.com\/...\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  - resource_type: <span class=\"hljs-string\">\"image\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  - format: <span class=\"hljs-string\">\"jpg\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  - tags: <span class=\"hljs-string\">\"sunset, beach, travel\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>```\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Once indexed by Smart Search AI, these Cloudinary assets can be retrieved alongside WordPress posts in unified search results.<br><\/p>\n\n\n\n<p><strong>WordPress Posts \u2192 Smart Search AI:<\/strong><\/p>\n\n\n\n<p>When you create a WordPress post via the <code>`wpengine--create-post`<\/code> tool, the post content is stored in WordPress. If you have Smart Search AI configured on your WordPress site, these posts are automatically indexed. Posts that include embedded Cloudinary images carry that image metadata along with the post content, creating rich, interconnected search results.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Unified Natural Language Search<\/h3>\n\n\n\n<p>Once both Cloudinary assets and WordPress posts are created via a natural language prompt and indexed, Smart Search AI can return results from both sources into a single query:<br><\/p>\n\n\n\n<p><strong>Scenario<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>User asks:\n<ul class=\"wp-block-list\">\n<li><em>&#8220;Find content about travel&#8221;<\/em><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Smart Search returns:\n<ul class=\"wp-block-list\">\n<li>Cloudinary images tagged with <em>&#8220;travel&#8221;<\/em><\/li>\n\n\n\n<li>WordPress posts about travel destinations<\/li>\n\n\n\n<li>Posts that include travel-related Cloudinary images<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>This unified search capability transforms your chatbot from a simple Q&amp;A interface into an intelligent content discovery and creation system.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"setting-up-wordpress-mcp-plugin\">Setting Up the WordPress MCP Server Plugin<\/h2>\n\n\n\n<p>Let&#8217;s install the WordPress MCP server that exposes WordPress functionality to our chatbot. This plugin implements the MCP protocol as a WordPress REST API endpoint, providing tools for post management and Smart Search AI indexing.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Installing the Plugin to WP Admin<\/h3>\n\n\n\n<p>Download the plugin from the GitHub repository:<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/Fran-A-Dev\/wpe-ssai-cloudinary-mcp\">https:\/\/github.com\/Fran-A-Dev\/wpe-ssai-cloudinary-mcp<\/a><\/p>\n\n\n\n<p>Once downloaded:<\/p>\n\n\n\n<p>1. Navigate to your WordPress admin dashboard<\/p>\n\n\n\n<p>2. Go to <strong><em>Plugins \u2192 Add New \u2192 Upload Plugin<\/em><\/strong><\/p>\n\n\n\n<p>3. Choose the downloaded ZIP file<\/p>\n\n\n\n<p>4. Click <em>&#8220;Install Now&#8221;<\/em><\/p>\n\n\n\n<p>5. Activate the plugin<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What does the plugin do?<\/h3>\n\n\n\n<p>The WP MCP Server plugin provides several key capabilities:<br><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">MCP Protocol Implementation<\/h4>\n\n\n\n<p>The plugin exposes a WordPress REST API endpoint at <code>`<em>\/wp-json\/wpengine\/v1\/mcp<\/em>` <\/code>that implements the Model Context Protocol. This endpoint handles JSON-RPC 2.0 requests from your Next.js chatbot, allowing the AI to discover and call WordPress tools dynamically.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">WordPress Post Management Tools:<\/h4>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>- <span class=\"hljs-string\">`wpengine--create-post`<\/span> - Creates WordPress posts <span class=\"hljs-keyword\">with<\/span> optional Cloudinary image embedding\n<\/span><\/span><span class='shcb-loc'><span>- <span class=\"hljs-string\">`wpengine--update-post`<\/span> - Updates existing posts\n<\/span><\/span><span class='shcb-loc'><span>- <span class=\"hljs-string\">`wpengine--get-post`<\/span> - Retrieves post details and metadata\n<\/span><\/span><span class='shcb-loc'><span>- <span class=\"hljs-string\">`wpengine--list-posts`<\/span> - Lists posts <span class=\"hljs-keyword\">with<\/span> filtering options\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h4 class=\"wp-block-heading\">Smart Search AI Indexing Tools:<\/h4>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>- <span class=\"hljs-string\">`wpengine--index-cloudinary-asset`<\/span> - Indexes individual Cloudinary assets into Smart Search AI via GraphQL mutations\n<\/span><\/span><span class='shcb-loc'><span>- <span class=\"hljs-string\">`wpengine--bulk-index-cloudinary-assets`<\/span> - Batch indexes multiple Cloudinary assets <span class=\"hljs-keyword\">for<\/span> efficient bulk operations\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h4 class=\"wp-block-heading\">Token-Based Authentication:<\/h4>\n\n\n\n<p>The plugin generates a secure access token on activation, ensuring only authorized MCP clients can call your WordPress tools. This token is passed via the `<code>x-mcp-token`<\/code> header in all requests.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Cloudinary Integration:<\/h4>\n\n\n\n<p>When creating posts with Cloudinary images, the plugin automatically embeds the image in the post content and stores Cloudinary metadata (<code>public_id, secure_url<\/code>) as post meta fields. This metadata enables rich search results and content relationships.<\/p>\n\n\n\n<p>To prepare your Cloudinary assets, make sure that you fill in the Tags, Title (caption), and Description (alt) fields with relevant keywords that describe your images in the metadata fields page. Smart Search AI indexes these fields, so an image tagged with <em><strong>&#8220;Star Wars, Jedi, Force&#8221;<\/strong><\/em> becomes searchable when users ask about related content.<\/p>\n\n\n\n<p>Here is what the metadata page looks like in your Cloudinary account:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"567\" src=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image-1024x567.png\" alt=\"\" class=\"wp-image-32124\" srcset=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image-1024x567.png 1024w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image-300x166.png 300w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image-768x425.png 768w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image-1536x851.png 1536w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>If you are unfamiliar with the Cloudinary admin console, please reference their <a href=\"https:\/\/cloudinary.com\/documentation\/programmable_media_guides\">guide here<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Plugin Configuration<\/h3>\n\n\n\n<p>After activating the plugin, navigate to <strong><em>Settings \u2192 WP Engine MCP <\/em><\/strong>in your WordPress admin. You&#8217;ll see three key pieces of information:<br><\/p>\n\n\n\n<p>1. MCP Endpoint URL<\/p>\n\n\n\n<p><a href=\"https:\/\/your-site.com\/wp-json\/wpengine\/v1\/mcp\">https:\/\/your-site.com\/wp-json\/wpengine\/v1\/mcp<\/a><\/p>\n\n\n\n<p>Copy this URL\u2014you&#8217;ll add it to your Next.js <code>`.env.local`<\/code> file as <code>`WORDPRESS_MCP_URL`<\/code>.<\/p>\n\n\n\n<p>2. MCP Access Token<\/p>\n\n\n\n<p>The plugin displays a generated access token. Click <strong>&#8220;Copy Token&#8221;<\/strong> and add it to your <code>`.env.local`<\/code> file as <code>`WORDPRESS_MCP_TOKEN`<\/code>.<\/p>\n\n\n\n<p>3. Smart Search AI Credentials<\/p>\n\n\n\n<p>Configure your Smart Search GraphQL endpoint URL and access token. These credentials allow the plugin to send indexing mutations to Smart Search AI. You can find these in your <strong><em>WordPress Admin \u2192 Smart Search \u2192 Settings<\/em><\/strong>.<\/p>\n\n\n\n<p>This is what you should be seeing:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"543\" src=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image-1-1024x543.png\" alt=\"\" class=\"wp-image-32125\" srcset=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image-1-1024x543.png 1024w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image-1-300x159.png 300w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image-1-768x408.png 768w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image-1-1536x815.png 1536w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image-1.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Once configured, the plugin is ready to receive MCP requests from your Next.js chatbot.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"refactoring-nextjs-route-handler\">Refactoring the Next.js Route Handler<\/h2>\n\n\n\n<p>Now lets update the Next.js API route to orchestrate all three MCP servers. The refactored <code>`route.ts`<\/code> file manages multiple MCP clients, builds tool wrappers with validation, and provides fallback mechanisms.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Environment Variables<\/h3>\n\n\n\n<p>Add these to your <code>`.env.local`<\/code> file:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>`.env\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"># Existing from previous tutorial<\/span>\n<\/span><\/span><span class='shcb-loc'><span>AI_TOOLKIT_MCP_URL=https:<span class=\"hljs-comment\">\/\/your-site-atlassearch.a.run.app\/mcp<\/span>\n<\/span><\/span><span class='shcb-loc'><span>GOOGLE_GENERATIVE_AI_API_KEY=your_gemini_api_key\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"># New additions<\/span>\n<\/span><\/span><span class='shcb-loc'><span>CLOUDINARY_MCP_URL=https:<span class=\"hljs-comment\">\/\/asset-management.mcp.cloudinary.com\/sse<\/span>\n<\/span><\/span><span class='shcb-loc'><span>CLOUDINARY_CLOUD_NAME=your_cloud_name\n<\/span><\/span><span class='shcb-loc'><span>CLOUDINARY_API_KEY=your_api_key\n<\/span><\/span><span class='shcb-loc'><span>CLOUDINARY_API_SECRET=your_api_secret\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>WORDPRESS_MCP_URL=https:<span class=\"hljs-comment\">\/\/your-site.com\/wp-json\/wpengine\/v1\/mcp<\/span>\n<\/span><\/span><span class='shcb-loc'><span>WORDPRESS_MCP_TOKEN=your_mcp_token_from_wordpress\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>A <code>.env.local<\/code> file stores local environment variables for a Next.js app, usually for app settings and secrets you do not want hardcoded into your codebase.<\/p>\n\n\n\n<p>The refactored route handler introduces several architectural improvements. Let&#8217;s walk through the main sections.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">MCP Client Caching:<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>```typescript\n<\/span><\/span><span class='shcb-loc'><span>let mcpClientsCache: {\n<\/span><\/span><span class='shcb-loc'><span>  smartSearch: any;\n<\/span><\/span><span class='shcb-loc'><span>  cloudinary: any;\n<\/span><\/span><span class='shcb-loc'><span>  wordpress: any;\n<\/span><\/span><span class='shcb-loc'><span>} | <span class=\"hljs-keyword\">null<\/span> = <span class=\"hljs-keyword\">null<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>async <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getMCPClients<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (mcpClientsCache) {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">return<\/span> mcpClientsCache;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>  \n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/\/ Initialize clients...<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  mcpClientsCache = {\n<\/span><\/span><span class='shcb-loc'><span>    smartSearch: smartSearchClient,\n<\/span><\/span><span class='shcb-loc'><span>    cloudinary: cloudinaryClient,\n<\/span><\/span><span class='shcb-loc'><span>    wordpress: wordpressClient,\n<\/span><\/span><span class='shcb-loc'><span>  };\n<\/span><\/span><span class='shcb-loc'><span>  \n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">return<\/span> mcpClientsCache;\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>```\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This caching strategy avoids recreating MCP connections on every request, significantly improving performance. The first request initializes all three clients, and subsequent requests reuse the cached connections.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">WordPress Tool Validation:<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>```typescript\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> createPostParameters = z.object({\n<\/span><\/span><span class='shcb-loc'><span>  title: z.string().min(<span class=\"hljs-number\">1<\/span>),\n<\/span><\/span><span class='shcb-loc'><span>  content: z.string().min(<span class=\"hljs-number\">1<\/span>),\n<\/span><\/span><span class='shcb-loc'><span>  status: z.enum(&#91;<span class=\"hljs-string\">\"publish\"<\/span>, <span class=\"hljs-string\">\"draft\"<\/span>, <span class=\"hljs-string\">\"pending\"<\/span>]).optional(),\n<\/span><\/span><span class='shcb-loc'><span>  cloudinary_url: z.string().optional(),\n<\/span><\/span><span class='shcb-loc'><span>  cloudinary_public_id: z.string().optional(),\n<\/span><\/span><span class='shcb-loc'><span>});\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">validateCreatePostArgs<\/span><span class=\"hljs-params\">(args: z.infer&lt;typeof createPostParameters&gt;)<\/span> <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (args.cloudinary_public_id &amp;&amp; !args.cloudinary_url) {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> Error(\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-string\">\"cloudinary_url is required to embed the image when cloudinary_public_id is provided.\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    );\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>```\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Zod schemas provide runtime validation of AI-generated tool parameters. This validation ensures that when Gemini attempts to create a WordPress post with a Cloudinary image, it must provide the <code>`cloudinary_url`<\/code> parameter\u2014preventing incomplete or malformed requests from reaching WordPress.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Fallback Mechanism:<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-string\">``<\/span><span class=\"hljs-string\">`typescript<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">let wordpressTools = await loadToolsSafely(<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">  wordpressClient,<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">  buildStableWordPressTools<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">);<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">if (Object.keys(wordpressTools).length === 0) {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">  wordpressTools = buildDirectWordPressFallbackTools(<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">    process.env.WORDPRESS_MCP_URL,<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">    process.env.WORDPRESS_MCP_TOKEN<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">  );<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">}<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">`<\/span><span class=\"hljs-string\">``<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><br>If the MCP client connection fails, the fallback mechanism makes direct HTTP calls to the WordPress MCP endpoint using the same JSON-RPC protocol. This resilience pattern ensures WordPress functionality remains available even if the MCP SDK encounters issues.<br><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">System Prompt Engineering:<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-string\">``<\/span><span class=\"hljs-string\">`typescript<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">const systemPromptContent = `<\/span>You are a helpful AI assistant <span class=\"hljs-keyword\">with<\/span> access to tools <span class=\"hljs-keyword\">for<\/span> searching data.\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>CRITICAL INSTRUCTIONS:\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-number\">1.<\/span> When users ask about Cloudinary, images, videos, media, or assets:\n<\/span><\/span><span class='shcb-loc'><span>   - You MUST use Cloudinary tools (search-assets, list-images, list-videos, etc.)\n<\/span><\/span><span class='shcb-loc'><span>   - NEVER respond without calling a Cloudinary tool first\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-number\">2.<\/span> When users ask about TV shows or knowledge retrieval:\n<\/span><\/span><span class='shcb-loc'><span>   - You MUST use the <span class=\"hljs-string\">'search'<\/span> tool\n<\/span><\/span><span class='shcb-loc'><span>   - NEVER respond without calling the search tool first\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-number\">3.<\/span> When users ask about WordPress posts, publishing, drafts, site info, or cache:\n<\/span><\/span><span class='shcb-loc'><span>   - You MUST use WordPress tools (wpengine--create-post, wpengine--list-posts, etc.)\n<\/span><\/span><span class='shcb-loc'><span>   - NEVER respond without calling a WordPress tool first\n<\/span><\/span><span class='shcb-loc'><span>   - If creating a post <span class=\"hljs-keyword\">with<\/span> a Cloudinary image, you MUST include cloudinary_url <span class=\"hljs-keyword\">in<\/span> wpengine--create-post <span class=\"hljs-built_in\">arguments<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>NEVER make up data. ALWAYS call the appropriate tool before responding.<span class=\"hljs-string\">`;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">`<\/span><span class=\"hljs-string\">``<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><br><\/p>\n\n\n\n<p>This system guides Gemini on when to use which tools, preventing the AI from hallucinating responses instead of calling the appropriate MCP tools. The explicit instructions ensure the AI orchestrates tools correctly for multi-step workflows.<br>Here is the entire <code>`<a href=\"http:\/\/route.ts\">route.ts<\/a>`<\/code> file here for your reference.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>code block <\/summary><pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ IMPORTANT! Set the runtime to edge<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> runtime = <span class=\"hljs-string\">\"edge\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  convertToCoreMessages,\n<\/span><\/span><span class='shcb-loc'><span>  experimental_createMCPClient,\n<\/span><\/span><span class='shcb-loc'><span>  Message,\n<\/span><\/span><span class='shcb-loc'><span>  streamText,\n<\/span><\/span><span class='shcb-loc'><span>  tool,\n<\/span><\/span><span class='shcb-loc'><span>} <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"ai\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> { createGoogleGenerativeAI } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@ai-sdk\/google\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> { z } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"zod\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> { weatherTool } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/app\/utils\/tools\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> { StreamableHTTPClientTransport } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@modelcontextprotocol\/sdk\/client\/streamableHttp.js\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\/**<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"> * Initialize the Google Generative AI API<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"> *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> google = createGoogleGenerativeAI();\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ MCP client cache to avoid recreating on every request<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">let<\/span> mcpClientsCache: {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">smartSearch<\/span>: any;\n<\/span><\/span><span class='shcb-loc'><span>  cloudinary: any;\n<\/span><\/span><span class='shcb-loc'><span>  wordpress: any;\n<\/span><\/span><span class='shcb-loc'><span>} | <span class=\"hljs-literal\">null<\/span> = <span class=\"hljs-literal\">null<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> createPostDescription =\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-string\">\"Create a WordPress post. If including a Cloudinary image, ALWAYS pass cloudinary_url (secure URL) so the image is embedded.\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> createPostParameters = z.object({\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">title<\/span>: z.string().min(<span class=\"hljs-number\">1<\/span>),\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">content<\/span>: z.string().min(<span class=\"hljs-number\">1<\/span>),\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">status<\/span>: z.enum(&#91;<span class=\"hljs-string\">\"publish\"<\/span>, <span class=\"hljs-string\">\"draft\"<\/span>, <span class=\"hljs-string\">\"pending\"<\/span>]).optional(),\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">cloudinary_url<\/span>: z.string().optional(),\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">cloudinary_public_id<\/span>: z.string().optional(),\n<\/span><\/span><span class='shcb-loc'><span>});\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> updatePostParameters = z.object({\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">post_id<\/span>: z.number(),\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">title<\/span>: z.string().optional(),\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">content<\/span>: z.string().optional(),\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">status<\/span>: z.string().optional(),\n<\/span><\/span><span class='shcb-loc'><span>});\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> getPostParameters = z.object({\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">post_id<\/span>: z.number(),\n<\/span><\/span><span class='shcb-loc'><span>});\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> listPostsParameters = z.object({\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">limit<\/span>: z.number().optional(),\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">status<\/span>: z.string().optional(),\n<\/span><\/span><span class='shcb-loc'><span>});\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> passthroughObjectParameters = z.object({}).passthrough();\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">validateCreatePostArgs<\/span>(<span class=\"hljs-params\">args: z.infer&lt;typeof createPostParameters&gt;<\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (args.cloudinary_public_id &amp;&amp; !args.cloudinary_url) {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-string\">\"cloudinary_url is required to embed the image when cloudinary_public_id is provided.\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    );\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">addHyphenAliases<\/span>(<span class=\"hljs-params\">tools: Record&lt;string, any&gt;, extraAliases: Record&lt;string, string&gt; = {}<\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> withAliases: Record&lt;string, any&gt; = { ...tools };\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> &#91;name, tool] <span class=\"hljs-keyword\">of<\/span> <span class=\"hljs-built_in\">Object<\/span>.entries(tools)) {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> alias = name.replace(<span class=\"hljs-regexp\">\/-\/g<\/span>, <span class=\"hljs-string\">\"_\"<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">if<\/span> (!(alias <span class=\"hljs-keyword\">in<\/span> withAliases)) {\n<\/span><\/span><span class='shcb-loc'><span>      withAliases&#91;alias] = tool;\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> &#91;alias, canonicalName] <span class=\"hljs-keyword\">of<\/span> <span class=\"hljs-built_in\">Object<\/span>.entries(extraAliases)) {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">if<\/span> (canonicalName <span class=\"hljs-keyword\">in<\/span> withAliases &amp;&amp; !(alias <span class=\"hljs-keyword\">in<\/span> withAliases)) {\n<\/span><\/span><span class='shcb-loc'><span>      withAliases&#91;alias] = withAliases&#91;canonicalName];\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">return<\/span> withAliases;\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">loadToolsSafely<\/span>(<span class=\"hljs-params\"><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-function\"><span class=\"hljs-params\">  client: any,<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-function\"><span class=\"hljs-params\">  builder: (rawTools: Record&lt;string, any&gt;<\/span>) =&gt; <span class=\"hljs-title\">Record<\/span>&lt;<span class=\"hljs-title\">string<\/span>, <span class=\"hljs-title\">any<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-function\">) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (!client) <span class=\"hljs-keyword\">return<\/span> {};\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">try<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> rawTools = <span class=\"hljs-keyword\">await<\/span> client.tools();\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">return<\/span> builder(rawTools);\n<\/span><\/span><span class='shcb-loc'><span>  } <span class=\"hljs-keyword\">catch<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">return<\/span> {};\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">buildStableSmartSearchTools<\/span>(<span class=\"hljs-params\">rawTools: Record&lt;string, any&gt;<\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> stableTools: Record&lt;string, any&gt; = {};\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (rawTools.search?.execute) {\n<\/span><\/span><span class='shcb-loc'><span>    stableTools.search = tool({\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">description<\/span>:\n<\/span><\/span><span class='shcb-loc'><span>        rawTools.search.description ||\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-string\">\"Search for relevant information and return ranked results.\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">parameters<\/span>: z.object({\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">query<\/span>: z.string().min(<span class=\"hljs-number\">1<\/span>),\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">filter<\/span>: z.string().optional(),\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">limit<\/span>: z.number().optional(),\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">offset<\/span>: z.number().optional(),\n<\/span><\/span><span class='shcb-loc'><span>      }),\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">execute<\/span>: <span class=\"hljs-keyword\">async<\/span> (args) =&gt; rawTools.search.execute(args),\n<\/span><\/span><span class='shcb-loc'><span>    });\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (rawTools.fetch?.execute) {\n<\/span><\/span><span class='shcb-loc'><span>    stableTools.fetch = tool({\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">description<\/span>:\n<\/span><\/span><span class='shcb-loc'><span>        rawTools.fetch.description ||\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-string\">\"Fetch a document by ID and return its full content.\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">parameters<\/span>: z.object({\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">id<\/span>: z.string().min(<span class=\"hljs-number\">1<\/span>),\n<\/span><\/span><span class='shcb-loc'><span>      }),\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">execute<\/span>: <span class=\"hljs-keyword\">async<\/span> (args) =&gt; rawTools.fetch.execute(args),\n<\/span><\/span><span class='shcb-loc'><span>    });\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">return<\/span> stableTools;\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">buildStableCloudinaryTools<\/span>(<span class=\"hljs-params\">rawTools: Record&lt;string, any&gt;<\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> stableTools: Record&lt;string, any&gt; = {};\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> cloudinaryToolNames = &#91;\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"search-assets\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"list-images\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"list-videos\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"list-files\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"get-asset-details\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"list-tags\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"visual-search-assets\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"transform-asset\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"get-tx-reference\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>  ];\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> name <span class=\"hljs-keyword\">of<\/span> cloudinaryToolNames) {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> raw = rawTools&#91;name];\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">if<\/span> (!raw?.execute) <span class=\"hljs-keyword\">continue<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">if<\/span> (name === <span class=\"hljs-string\">\"search-assets\"<\/span>) {\n<\/span><\/span><span class='shcb-loc'><span>      stableTools&#91;name] = tool({\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">description<\/span>:\n<\/span><\/span><span class='shcb-loc'><span>          raw.description ||\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-string\">\"Search Cloudinary assets. Supports plain query text and advanced request payload.\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">parameters<\/span>: z.object({\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-attr\">query<\/span>: z.string().optional(),\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-attr\">expression<\/span>: z.string().optional(),\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-attr\">max_results<\/span>: z.number().optional(),\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-attr\">next_cursor<\/span>: z.string().optional(),\n<\/span><\/span><span class='shcb-loc'><span>        }).passthrough(),\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">execute<\/span>: <span class=\"hljs-keyword\">async<\/span> (args) =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">const<\/span> expression = args.expression || args.query || <span class=\"hljs-string\">\"\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">return<\/span> raw.execute({\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-attr\">request<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>              expression,\n<\/span><\/span><span class='shcb-loc'><span>              ...(args.max_results ? { <span class=\"hljs-attr\">max_results<\/span>: args.max_results } : {}),\n<\/span><\/span><span class='shcb-loc'><span>              ...(args.next_cursor ? { <span class=\"hljs-attr\">next_cursor<\/span>: args.next_cursor } : {}),\n<\/span><\/span><span class='shcb-loc'><span>            },\n<\/span><\/span><span class='shcb-loc'><span>          });\n<\/span><\/span><span class='shcb-loc'><span>        },\n<\/span><\/span><span class='shcb-loc'><span>      });\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">continue<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    stableTools&#91;name] = tool({\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">description<\/span>: raw.description || <span class=\"hljs-string\">`Cloudinary tool: <span class=\"hljs-subst\">${name}<\/span>`<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-comment\">\/\/ Force OBJECT schema for Gemini compatibility.<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">parameters<\/span>: passthroughObjectParameters,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">execute<\/span>: <span class=\"hljs-keyword\">async<\/span> (args) =&gt; raw.execute(args ?? {}),\n<\/span><\/span><span class='shcb-loc'><span>    });\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">return<\/span> stableTools;\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">buildStableWordPressTools<\/span>(<span class=\"hljs-params\">rawTools: Record&lt;string, any&gt;<\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> stableTools: Record&lt;string, any&gt; = {};\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> wordpressToolNames = &#91;\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"wpengine--get-current-site-info\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"wpengine--purge-cache\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"wpengine--create-post\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"wpengine--update-post\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"wpengine--get-post\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"wpengine--list-posts\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"wpengine--index-cloudinary-asset\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"wpengine--bulk-index-cloudinary-assets\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>  ];\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> name <span class=\"hljs-keyword\">of<\/span> wordpressToolNames) {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> raw = rawTools&#91;name];\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">if<\/span> (!raw?.execute) <span class=\"hljs-keyword\">continue<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">if<\/span> (name === <span class=\"hljs-string\">\"wpengine--create-post\"<\/span>) {\n<\/span><\/span><span class='shcb-loc'><span>      stableTools&#91;name] = tool({\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">description<\/span>: raw.description || createPostDescription,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">parameters<\/span>: createPostParameters,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">execute<\/span>: <span class=\"hljs-keyword\">async<\/span> (args) =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>          validateCreatePostArgs(args);\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">return<\/span> raw.execute(args);\n<\/span><\/span><span class='shcb-loc'><span>        },\n<\/span><\/span><span class='shcb-loc'><span>      });\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">continue<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">if<\/span> (name === <span class=\"hljs-string\">\"wpengine--update-post\"<\/span>) {\n<\/span><\/span><span class='shcb-loc'><span>      stableTools&#91;name] = tool({\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">description<\/span>: raw.description || <span class=\"hljs-string\">\"Update an existing WordPress post.\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">parameters<\/span>: updatePostParameters,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">execute<\/span>: <span class=\"hljs-keyword\">async<\/span> (args) =&gt; raw.execute(args),\n<\/span><\/span><span class='shcb-loc'><span>      });\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">continue<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">if<\/span> (name === <span class=\"hljs-string\">\"wpengine--get-post\"<\/span>) {\n<\/span><\/span><span class='shcb-loc'><span>      stableTools&#91;name] = tool({\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">description<\/span>: raw.description || <span class=\"hljs-string\">\"Get details of a WordPress post by ID.\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">parameters<\/span>: getPostParameters,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">execute<\/span>: <span class=\"hljs-keyword\">async<\/span> (args) =&gt; raw.execute(args),\n<\/span><\/span><span class='shcb-loc'><span>      });\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">continue<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">if<\/span> (name === <span class=\"hljs-string\">\"wpengine--list-posts\"<\/span>) {\n<\/span><\/span><span class='shcb-loc'><span>      stableTools&#91;name] = tool({\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">description<\/span>: raw.description || <span class=\"hljs-string\">\"List WordPress posts.\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">parameters<\/span>: listPostsParameters,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">execute<\/span>: <span class=\"hljs-keyword\">async<\/span> (args) =&gt; raw.execute(args ?? {}),\n<\/span><\/span><span class='shcb-loc'><span>      });\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">continue<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    stableTools&#91;name] = tool({\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">description<\/span>: raw.description || <span class=\"hljs-string\">`WordPress tool: <span class=\"hljs-subst\">${name}<\/span>`<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">parameters<\/span>: passthroughObjectParameters,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">execute<\/span>: <span class=\"hljs-keyword\">async<\/span> (args) =&gt; raw.execute(args ?? {}),\n<\/span><\/span><span class='shcb-loc'><span>    });\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">return<\/span> stableTools;\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">buildDirectWordPressFallbackTools<\/span>(<span class=\"hljs-params\">wordpressMcpUrl?: string, wordpressMcpToken?: string<\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (!wordpressMcpUrl || !wordpressMcpToken) {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">return<\/span> {};\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> callWordPressTool = <span class=\"hljs-keyword\">async<\/span> (name: string, <span class=\"hljs-attr\">args<\/span>: Record&lt;string, any&gt;) =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> fetch(wordpressMcpUrl, {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">\"POST\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">headers<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-string\">\"Content-Type\"<\/span>: <span class=\"hljs-string\">\"application\/json\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-string\">\"x-mcp-token\"<\/span>: wordpressMcpToken,\n<\/span><\/span><span class='shcb-loc'><span>      },\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">body<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.stringify({\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">jsonrpc<\/span>: <span class=\"hljs-string\">\"2.0\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">id<\/span>: <span class=\"hljs-built_in\">Date<\/span>.now(),\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">\"tools\/call\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">params<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>          name,\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-attr\">arguments<\/span>: args ?? {},\n<\/span><\/span><span class='shcb-loc'><span>        },\n<\/span><\/span><span class='shcb-loc'><span>      }),\n<\/span><\/span><span class='shcb-loc'><span>    });\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> rawText = <span class=\"hljs-keyword\">await<\/span> response.text();\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">let<\/span> payload: any;\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">try<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>      payload = <span class=\"hljs-built_in\">JSON<\/span>.parse(rawText);\n<\/span><\/span><span class='shcb-loc'><span>    } <span class=\"hljs-keyword\">catch<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(<span class=\"hljs-string\">`WordPress MCP returned non-JSON response (status <span class=\"hljs-subst\">${response.status}<\/span>)`<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">if<\/span> (!response.ok || payload?.error) {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(\n<\/span><\/span><span class='shcb-loc'><span>        payload?.error?.message || <span class=\"hljs-string\">`WordPress MCP request failed (status <span class=\"hljs-subst\">${response.status}<\/span>)`<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      );\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">return<\/span> payload?.result ?? payload;\n<\/span><\/span><span class='shcb-loc'><span>  };\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">return<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"wpengine--create-post\"<\/span>: tool({\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">description<\/span>: createPostDescription,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">parameters<\/span>: createPostParameters,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">execute<\/span>: <span class=\"hljs-keyword\">async<\/span> (args) =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>        validateCreatePostArgs(args);\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-keyword\">return<\/span> callWordPressTool(<span class=\"hljs-string\">\"wpengine--create-post\"<\/span>, args);\n<\/span><\/span><span class='shcb-loc'><span>      },\n<\/span><\/span><span class='shcb-loc'><span>    }),\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"wpengine--update-post\"<\/span>: tool({\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">description<\/span>: <span class=\"hljs-string\">\"Update an existing WordPress post.\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">parameters<\/span>: updatePostParameters,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">execute<\/span>: <span class=\"hljs-keyword\">async<\/span> (args) =&gt; callWordPressTool(<span class=\"hljs-string\">\"wpengine--update-post\"<\/span>, args),\n<\/span><\/span><span class='shcb-loc'><span>    }),\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"wpengine--get-post\"<\/span>: tool({\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">description<\/span>: <span class=\"hljs-string\">\"Get details of a specific WordPress post.\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">parameters<\/span>: getPostParameters,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">execute<\/span>: <span class=\"hljs-keyword\">async<\/span> (args) =&gt; callWordPressTool(<span class=\"hljs-string\">\"wpengine--get-post\"<\/span>, args),\n<\/span><\/span><span class='shcb-loc'><span>    }),\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"wpengine--list-posts\"<\/span>: tool({\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">description<\/span>: <span class=\"hljs-string\">\"List WordPress posts.\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">parameters<\/span>: listPostsParameters,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">execute<\/span>: <span class=\"hljs-keyword\">async<\/span> (args) =&gt; callWordPressTool(<span class=\"hljs-string\">\"wpengine--list-posts\"<\/span>, args ?? {}),\n<\/span><\/span><span class='shcb-loc'><span>    }),\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">\"wpengine--get-current-site-info\"<\/span>: tool({\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">description<\/span>: <span class=\"hljs-string\">\"Get information about the current WordPress site.\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">parameters<\/span>: passthroughObjectParameters,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">execute<\/span>: <span class=\"hljs-keyword\">async<\/span> () =&gt; callWordPressTool(<span class=\"hljs-string\">\"wpengine--get-current-site-info\"<\/span>, {}),\n<\/span><\/span><span class='shcb-loc'><span>    }),\n<\/span><\/span><span class='shcb-loc'><span>  };\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getMCPClients<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (mcpClientsCache) {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">return<\/span> mcpClientsCache;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/\/ Smart Search MCP Client<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> smartSearchTransport = <span class=\"hljs-keyword\">new<\/span> StreamableHTTPClientTransport(\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">new<\/span> URL(process.env.AI_TOOLKIT_MCP_URL || <span class=\"hljs-string\">\"http:\/\/localhost:8080\/mcp\"<\/span>)\n<\/span><\/span><span class='shcb-loc'><span>  );\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> smartSearchClient = <span class=\"hljs-keyword\">await<\/span> experimental_createMCPClient({\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">transport<\/span>: smartSearchTransport,\n<\/span><\/span><span class='shcb-loc'><span>  });\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/\/ Cloudinary MCP Client (Remote)<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> cloudinaryMcpUrl = process.env.CLOUDINARY_MCP_URL || <span class=\"hljs-string\">\"https:\/\/asset-management.mcp.cloudinary.com\/sse\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">let<\/span> cloudinaryClient = <span class=\"hljs-literal\">null<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">let<\/span> wordpressClient = <span class=\"hljs-literal\">null<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">try<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> cloudinaryTransport = <span class=\"hljs-keyword\">new<\/span> StreamableHTTPClientTransport(\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">new<\/span> URL(cloudinaryMcpUrl),\n<\/span><\/span><span class='shcb-loc'><span>      {\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">fetch<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">url: string | URL, init?: RequestInit<\/span>) =&gt;<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">const<\/span> headers: Record&lt;string, string&gt; = {\n<\/span><\/span><span class='shcb-loc'><span>            ...init?.headers <span class=\"hljs-keyword\">as<\/span> Record&lt;string, string&gt;,\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-string\">'Accept'<\/span>: <span class=\"hljs-string\">'application\/json, text\/event-stream'<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-string\">'Content-Type'<\/span>: <span class=\"hljs-string\">'application\/json'<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>          };\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-comment\">\/\/ Add Cloudinary credentials if provided<\/span>\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">if<\/span> (process.env.CLOUDINARY_CLOUD_NAME) {\n<\/span><\/span><span class='shcb-loc'><span>            headers&#91;<span class=\"hljs-string\">'cloudinary-cloud-name'<\/span>] = process.env.CLOUDINARY_CLOUD_NAME;\n<\/span><\/span><span class='shcb-loc'><span>          }\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">if<\/span> (process.env.CLOUDINARY_API_KEY) {\n<\/span><\/span><span class='shcb-loc'><span>            headers&#91;<span class=\"hljs-string\">'cloudinary-api-key'<\/span>] = process.env.CLOUDINARY_API_KEY;\n<\/span><\/span><span class='shcb-loc'><span>          }\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">if<\/span> (process.env.CLOUDINARY_API_SECRET) {\n<\/span><\/span><span class='shcb-loc'><span>            headers&#91;<span class=\"hljs-string\">'cloudinary-api-secret'<\/span>] = process.env.CLOUDINARY_API_SECRET;\n<\/span><\/span><span class='shcb-loc'><span>          }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">return<\/span> fetch(url, {\n<\/span><\/span><span class='shcb-loc'><span>            ...init,\n<\/span><\/span><span class='shcb-loc'><span>            headers\n<\/span><\/span><span class='shcb-loc'><span>          });\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>      } <span class=\"hljs-keyword\">as<\/span> any\n<\/span><\/span><span class='shcb-loc'><span>    );\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    cloudinaryClient = <span class=\"hljs-keyword\">await<\/span> experimental_createMCPClient({\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">transport<\/span>: cloudinaryTransport,\n<\/span><\/span><span class='shcb-loc'><span>    });\n<\/span><\/span><span class='shcb-loc'><span>  } <span class=\"hljs-keyword\">catch<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    cloudinaryClient = <span class=\"hljs-literal\">null<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> wordpressMcpUrl = process.env.WORDPRESS_MCP_URL;\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> wordpressMcpToken = process.env.WORDPRESS_MCP_TOKEN;\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (wordpressMcpUrl &amp;&amp; wordpressMcpToken) {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">try<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">const<\/span> wordpressTransport = <span class=\"hljs-keyword\">new<\/span> StreamableHTTPClientTransport(\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-keyword\">new<\/span> URL(wordpressMcpUrl),\n<\/span><\/span><span class='shcb-loc'><span>        {\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-attr\">fetch<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">url: string | URL, init?: RequestInit<\/span>) =&gt;<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-keyword\">const<\/span> headers: Record&lt;string, string&gt; = {\n<\/span><\/span><span class='shcb-loc'><span>              ...(init?.headers <span class=\"hljs-keyword\">as<\/span> Record&lt;string, string&gt;),\n<\/span><\/span><span class='shcb-loc'><span>              <span class=\"hljs-attr\">Accept<\/span>: <span class=\"hljs-string\">\"application\/json, text\/event-stream\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>              <span class=\"hljs-string\">\"Content-Type\"<\/span>: <span class=\"hljs-string\">\"application\/json\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>              <span class=\"hljs-string\">\"x-mcp-token\"<\/span>: wordpressMcpToken,\n<\/span><\/span><span class='shcb-loc'><span>            };\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-keyword\">return<\/span> fetch(url, {\n<\/span><\/span><span class='shcb-loc'><span>              ...init,\n<\/span><\/span><span class='shcb-loc'><span>              headers,\n<\/span><\/span><span class='shcb-loc'><span>            });\n<\/span><\/span><span class='shcb-loc'><span>          },\n<\/span><\/span><span class='shcb-loc'><span>        } <span class=\"hljs-keyword\">as<\/span> any\n<\/span><\/span><span class='shcb-loc'><span>      );\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>      wordpressClient = <span class=\"hljs-keyword\">await<\/span> experimental_createMCPClient({\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">transport<\/span>: wordpressTransport,\n<\/span><\/span><span class='shcb-loc'><span>      });\n<\/span><\/span><span class='shcb-loc'><span>    } <span class=\"hljs-keyword\">catch<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>      wordpressClient = <span class=\"hljs-literal\">null<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  mcpClientsCache = {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">smartSearch<\/span>: smartSearchClient,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">cloudinary<\/span>: cloudinaryClient,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">wordpress<\/span>: wordpressClient,\n<\/span><\/span><span class='shcb-loc'><span>  };\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">return<\/span> mcpClientsCache;\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">POST<\/span>(<span class=\"hljs-params\">req: Request<\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">try<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-comment\">\/\/ Get MCP clients<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">smartSearch<\/span>: smartSearchClient,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">cloudinary<\/span>: cloudinaryClient,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">wordpress<\/span>: wordpressClient,\n<\/span><\/span><span class='shcb-loc'><span>    } = <span class=\"hljs-keyword\">await<\/span> getMCPClients();\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-comment\">\/\/ Get tools from Smart Search MCP<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> rawSmartSearchTools = <span class=\"hljs-keyword\">await<\/span> smartSearchClient.tools();\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> smartSearchTools = buildStableSmartSearchTools(rawSmartSearchTools);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-comment\">\/\/ Get tools from Cloudinary MCP if connected<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> cloudinaryTools = <span class=\"hljs-keyword\">await<\/span> loadToolsSafely(\n<\/span><\/span><span class='shcb-loc'><span>      cloudinaryClient,\n<\/span><\/span><span class='shcb-loc'><span>      buildStableCloudinaryTools\n<\/span><\/span><span class='shcb-loc'><span>    );\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-comment\">\/\/ Get tools from WordPress MCP if connected<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">let<\/span> wordpressTools = <span class=\"hljs-keyword\">await<\/span> loadToolsSafely(\n<\/span><\/span><span class='shcb-loc'><span>      wordpressClient,\n<\/span><\/span><span class='shcb-loc'><span>      buildStableWordPressTools\n<\/span><\/span><span class='shcb-loc'><span>    );\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">if<\/span> (<span class=\"hljs-built_in\">Object<\/span>.keys(wordpressTools).length === <span class=\"hljs-number\">0<\/span>) {\n<\/span><\/span><span class='shcb-loc'><span>      wordpressTools = buildDirectWordPressFallbackTools(\n<\/span><\/span><span class='shcb-loc'><span>        process.env.WORDPRESS_MCP_URL,\n<\/span><\/span><span class='shcb-loc'><span>        process.env.WORDPRESS_MCP_TOKEN\n<\/span><\/span><span class='shcb-loc'><span>      );\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> { messages }: { <span class=\"hljs-attr\">messages<\/span>: <span class=\"hljs-built_in\">Array<\/span>&lt;Message&gt; } = <span class=\"hljs-keyword\">await<\/span> req.json();\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> coreMessages = convertToCoreMessages(messages);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> systemPromptContent = <span class=\"hljs-string\">`You are a helpful AI assistant with access to tools for searching data.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">CRITICAL INSTRUCTIONS:<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">1. When users ask about Cloudinary, images, videos, media, or assets:<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">   - You MUST use Cloudinary tools (search-assets, list-images, list-videos, etc.)<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">   - NEVER respond without calling a Cloudinary tool first<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">   - Example queries: \"show images\", \"find assets\", \"list videos\", \"search for tag\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">2. When users ask about TV shows or knowledge retrieval:<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">   - You MUST use the 'search' tool<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">   - NEVER respond without calling the search tool first<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">3. When users ask about WordPress posts, publishing, drafts, site info, or cache:<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">   - You MUST use WordPress tools (wpengine--create-post, wpengine--list-posts, etc.)<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">   - NEVER respond without calling a WordPress tool first<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">   - If creating a post with a Cloudinary image, you MUST include cloudinary_url in wpengine--create-post arguments<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">4. When users ask about weather:<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">   - You MUST use the weatherTool<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">NEVER make up data. ALWAYS call the appropriate tool before responding.`<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> cloudinaryToolsWithAliases = addHyphenAliases(cloudinaryTools);\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> wordpressToolsWithAliases = addHyphenAliases(wordpressTools, {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">post<\/span>: <span class=\"hljs-string\">\"wpengine--create-post\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    });\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> allTools = {\n<\/span><\/span><span class='shcb-loc'><span>      ...cloudinaryToolsWithAliases,\n<\/span><\/span><span class='shcb-loc'><span>      ...wordpressToolsWithAliases,\n<\/span><\/span><span class='shcb-loc'><span>      ...smartSearchTools,\n<\/span><\/span><span class='shcb-loc'><span>      weatherTool,\n<\/span><\/span><span class='shcb-loc'><span>    };\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> response = streamText({\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">model<\/span>: google(<span class=\"hljs-string\">\"models\/gemini-2.0-flash\"<\/span>, {\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">useSearchGrounding<\/span>: <span class=\"hljs-literal\">false<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>      }),\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">system<\/span>: systemPromptContent,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">messages<\/span>: coreMessages,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">tools<\/span>: allTools,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">maxSteps<\/span>: <span class=\"hljs-number\">5<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    });\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-comment\">\/\/ Convert the response into a friendly text-stream<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">return<\/span> response.toDataStreamResponse({\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">getErrorMessage<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">error<\/span>) =&gt;<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-keyword\">const<\/span> message =\n<\/span><\/span><span class='shcb-loc'><span>          error <span class=\"hljs-keyword\">instanceof<\/span> <span class=\"hljs-built_in\">Error<\/span> ? error.message : <span class=\"hljs-string\">\"Unknown streaming error\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">\"&#91;streamText error]\"<\/span>, error);\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">`Stream error: <span class=\"hljs-subst\">${message}<\/span>`<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>      },\n<\/span><\/span><span class='shcb-loc'><span>    });\n<\/span><\/span><span class='shcb-loc'><span>  } <span class=\"hljs-keyword\">catch<\/span> (e) {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">'&#91;API Error]'<\/span>, e);\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">throw<\/span> e;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/details>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"testing-multi-mcp-setup\">Testing Multi-MCP Setup<\/h2>\n\n\n\n<p>Let&#8217;s test the chatbot locally. Run <code>`npm run dev`<\/code> and navigate to <a href=\"http:\/\/localhost:3000\">http:\/\/localhost:3000<\/a>.&nbsp;<\/p>\n\n\n\n<p>I will use this prompt:&nbsp; <em>&#8220;Find a Return of the Jedi image in Cloudinary, create a WordPress post with the best one, then search for all Return Of The Jedi content. You can create the title and make sure the content is at least 3 paragraphs. After you create the post, send me the link to the live post&#8221;<\/em><\/p>\n\n\n\n<p><strong>Expected behavior:<\/strong><\/p>\n\n\n\n<p>1. <code>`search-assets`<\/code> (Cloudinary MCP) \u2192 finds Return of the Jedi images<\/p>\n\n\n\n<p>2. <code>`wpengine--index-cloudinary-asset`<\/code> (WordPress MCP) \u2192 indexes chosen image to Smart Search<\/p>\n\n\n\n<p>3. <code>`wpengine--create-post`<\/code> (WordPress MCP) \u2192 creates post with embedded image<\/p>\n\n\n\n<p>4. WordPress automatically indexes the new post to Smart Search<\/p>\n\n\n\n<p>5. <code>`search` <\/code>(Smart Search MCP) with query &#8220;Return of the Jedi&#8221;<\/p>\n\n\n\n<p>6. Returns BOTH the newly created post AND the indexed Cloudinary asset<\/p>\n\n\n\n<p>This is what it should look like:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1920\" height=\"1080\" src=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/cloudinary-in-concert-mcp.gif\" alt=\"\" class=\"wp-image-32127\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\">Conclusion<\/h2>\n\n\n\n<p>We&#8217;ve extended the Smart Search RAG chatbot with WordPress and Cloudinary MCP integration, creating a system where natural language prompts can use complex workflows across multiple services. The WordPress MCP server plugin exposes post management and Smart Search indexing tools, while the refactored Next.js route coordinates three MCP servers in concert.<\/p>\n\n\n\n<p>Smart Search AI serves as the unified search layer, making both Cloudinary assets and WordPress posts discoverable through conversational queries. This architecture transforms your chatbot from a simple Q&amp;A interface into an intelligent content discovery and creation system.<\/p>\n\n\n\n<p>As always, we&#8217;re stoked to hear your feedback and learn about the projects you&#8217;re building with multi-MCP server orchestration! Share your work in the<a href=\"https:\/\/wpeng.in\/devrel-discord\/\"> Headless WordPress Discord<\/a>!<br><\/p>\n\n\n\n<p class=\"has-tiny-font-size\">[1] WP Engine is a proud member and supporter of the community of WordPress\u00ae users. The WordPress\u00ae trademark is the intellectual property of the WordPress Foundation. Uses of the WordPress\u00ae trademarks in this website are for identification purposes only and do not imply an endorsement by WordPress Foundation. WP Engine is not endorsed or owned by, or affiliated with, the WordPress Foundation.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>When building AI applications, you often face a fragmented data problem. Images live in Cloudinary, blog posts exist in WordPress\u00ae\u00b9, and each system has its own search. Users can&#8217;t ask [&hellip;]<\/p>\n","protected":false},"author":20,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_EventAllDay":false,"_EventTimezone":"","_EventStartDate":"","_EventEndDate":"","_EventStartDateUTC":"","_EventEndDateUTC":"","_EventShowMap":false,"_EventShowMapLink":false,"_EventURL":"","_EventCost":"","_EventCostDescription":"","_EventCurrencySymbol":"","_EventCurrencyCode":"","_EventCurrencyPosition":"","_EventDateTimeSeparator":"","_EventTimeRangeSeparator":"","_EventOrganizerID":[],"_EventVenueID":[],"_OrganizerEmail":"","_OrganizerPhone":"","_OrganizerWebsite":"","_VenueAddress":"","_VenueCity":"","_VenueCountry":"","_VenueProvince":"","_VenueState":"","_VenueZip":"","_VenuePhone":"","_VenueURL":"","_VenueStateProvince":"","_VenueLat":"","_VenueLng":"","_VenueShowMap":false,"_VenueShowMapLink":false,"footnotes":""},"categories":[23],"tags":[],"class_list":["post-32123","post","type-post","status-publish","format-standard","hentry","category-headless"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Using MCP Servers in Concert: Smart Search AI, WordPress\u00ae, and Cloudinary Integration - Builders<\/title>\n<meta name=\"description\" content=\"Learn how MCP servers connect WordPress\u00ae, Smart Search AI, and Cloudinary to enable intelligent content and media workflows across systems.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Using MCP Servers in concert with WordPress\u00ae, Smart Search AI, and Cloudinary\" \/>\n<meta property=\"og:description\" content=\"Learn how MCP servers connect WordPress\u00ae, Smart Search AI, and Cloudinary to enable intelligent content and media workflows across systems.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/\" \/>\n<meta property=\"og:site_name\" content=\"Builders\" \/>\n<meta property=\"article:published_time\" content=\"2026-04-02T19:51:59+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-04-24T15:34:40+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/mcp-smart-search-wordpress-cloudinary-scaled.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1440\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Francis Agulto\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@wpebuilders\" \/>\n<meta name=\"twitter:site\" content=\"@wpebuilders\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Francis Agulto\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/mcp-wordpress-cloudinary-smart-search\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/mcp-wordpress-cloudinary-smart-search\\\/\"},\"author\":{\"name\":\"Francis Agulto\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/person\\\/bcdcb4ac0b215c34b6b30e440a24dc54\"},\"headline\":\"Using MCP Servers in Concert: Smart Search AI, WordPress\u00ae, and Cloudinary Integration\",\"datePublished\":\"2026-04-02T19:51:59+00:00\",\"dateModified\":\"2026-04-24T15:34:40+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/mcp-wordpress-cloudinary-smart-search\\\/\"},\"wordCount\":1765,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/mcp-wordpress-cloudinary-smart-search\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/wp-content\\\/uploads\\\/2026\\\/04\\\/image-1024x567.png\",\"articleSection\":[\"Headless\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/wpengine.com\\\/builders\\\/mcp-wordpress-cloudinary-smart-search\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/mcp-wordpress-cloudinary-smart-search\\\/\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/mcp-wordpress-cloudinary-smart-search\\\/\",\"name\":\"Using MCP Servers in Concert: Smart Search AI, WordPress\u00ae, and Cloudinary Integration - Builders\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/mcp-wordpress-cloudinary-smart-search\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/mcp-wordpress-cloudinary-smart-search\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/wp-content\\\/uploads\\\/2026\\\/04\\\/image-1024x567.png\",\"datePublished\":\"2026-04-02T19:51:59+00:00\",\"dateModified\":\"2026-04-24T15:34:40+00:00\",\"description\":\"Learn how MCP servers connect WordPress\u00ae, Smart Search AI, and Cloudinary to enable intelligent content and media workflows across systems.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/mcp-wordpress-cloudinary-smart-search\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/wpengine.com\\\/builders\\\/mcp-wordpress-cloudinary-smart-search\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/mcp-wordpress-cloudinary-smart-search\\\/#primaryimage\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/wp-content\\\/uploads\\\/2026\\\/04\\\/image.png\",\"contentUrl\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/wp-content\\\/uploads\\\/2026\\\/04\\\/image.png\",\"width\":1600,\"height\":886},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/mcp-wordpress-cloudinary-smart-search\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Using MCP Servers in Concert: Smart Search AI, WordPress\u00ae, and Cloudinary Integration\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#website\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/\",\"name\":\"Builders\",\"description\":\"Reimagining the way we build with WordPress.\",\"publisher\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#organization\",\"name\":\"WP Engine\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/wp-content\\\/uploads\\\/2024\\\/05\\\/WP-Engine-Horizontal@2x.png\",\"contentUrl\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/wp-content\\\/uploads\\\/2024\\\/05\\\/WP-Engine-Horizontal@2x.png\",\"width\":348,\"height\":68,\"caption\":\"WP Engine\"},\"image\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/wpebuilders\",\"https:\\\/\\\/www.youtube.com\\\/channel\\\/UCh1WuL54XFb9ZI6m6goFv1g\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/person\\\/bcdcb4ac0b215c34b6b30e440a24dc54\",\"name\":\"Francis Agulto\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g\",\"caption\":\"Francis Agulto\"},\"description\":\"Fran Agulto is a Developer Advocate at WP Engine. He is a lover of all things headless WordPress, Rock Climbing, and overall being stoked for people that love what they do and share that stoke with others! Follow me on Twitter for cool stoked headless WP!\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/author\\\/francis-agultowpengine-com-2-2-2-2-2-2-2-2-2-2-2-3\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Using MCP Servers in Concert: Smart Search AI, WordPress\u00ae, and Cloudinary Integration - Builders","description":"Learn how MCP servers connect WordPress\u00ae, Smart Search AI, and Cloudinary to enable intelligent content and media workflows across systems.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/","og_locale":"en_US","og_type":"article","og_title":"Using MCP Servers in concert with WordPress\u00ae, Smart Search AI, and Cloudinary","og_description":"Learn how MCP servers connect WordPress\u00ae, Smart Search AI, and Cloudinary to enable intelligent content and media workflows across systems.","og_url":"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/","og_site_name":"Builders","article_published_time":"2026-04-02T19:51:59+00:00","article_modified_time":"2026-04-24T15:34:40+00:00","og_image":[{"width":2560,"height":1440,"url":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/mcp-smart-search-wordpress-cloudinary-scaled.png","type":"image\/png"}],"author":"Francis Agulto","twitter_card":"summary_large_image","twitter_creator":"@wpebuilders","twitter_site":"@wpebuilders","twitter_misc":{"Written by":"Francis Agulto","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/#article","isPartOf":{"@id":"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/"},"author":{"name":"Francis Agulto","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/person\/bcdcb4ac0b215c34b6b30e440a24dc54"},"headline":"Using MCP Servers in Concert: Smart Search AI, WordPress\u00ae, and Cloudinary Integration","datePublished":"2026-04-02T19:51:59+00:00","dateModified":"2026-04-24T15:34:40+00:00","mainEntityOfPage":{"@id":"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/"},"wordCount":1765,"commentCount":0,"publisher":{"@id":"https:\/\/wpengine.com\/builders\/#organization"},"image":{"@id":"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/#primaryimage"},"thumbnailUrl":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image-1024x567.png","articleSection":["Headless"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/","url":"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/","name":"Using MCP Servers in Concert: Smart Search AI, WordPress\u00ae, and Cloudinary Integration - Builders","isPartOf":{"@id":"https:\/\/wpengine.com\/builders\/#website"},"primaryImageOfPage":{"@id":"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/#primaryimage"},"image":{"@id":"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/#primaryimage"},"thumbnailUrl":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image-1024x567.png","datePublished":"2026-04-02T19:51:59+00:00","dateModified":"2026-04-24T15:34:40+00:00","description":"Learn how MCP servers connect WordPress\u00ae, Smart Search AI, and Cloudinary to enable intelligent content and media workflows across systems.","breadcrumb":{"@id":"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/#primaryimage","url":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image.png","contentUrl":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2026\/04\/image.png","width":1600,"height":886},{"@type":"BreadcrumbList","@id":"https:\/\/wpengine.com\/builders\/mcp-wordpress-cloudinary-smart-search\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/wpengine.com\/builders\/"},{"@type":"ListItem","position":2,"name":"Using MCP Servers in Concert: Smart Search AI, WordPress\u00ae, and Cloudinary Integration"}]},{"@type":"WebSite","@id":"https:\/\/wpengine.com\/builders\/#website","url":"https:\/\/wpengine.com\/builders\/","name":"Builders","description":"Reimagining the way we build with WordPress.","publisher":{"@id":"https:\/\/wpengine.com\/builders\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/wpengine.com\/builders\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/wpengine.com\/builders\/#organization","name":"WP Engine","url":"https:\/\/wpengine.com\/builders\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/logo\/image\/","url":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2024\/05\/WP-Engine-Horizontal@2x.png","contentUrl":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2024\/05\/WP-Engine-Horizontal@2x.png","width":348,"height":68,"caption":"WP Engine"},"image":{"@id":"https:\/\/wpengine.com\/builders\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/wpebuilders","https:\/\/www.youtube.com\/channel\/UCh1WuL54XFb9ZI6m6goFv1g"]},{"@type":"Person","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/person\/bcdcb4ac0b215c34b6b30e440a24dc54","name":"Francis Agulto","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g","caption":"Francis Agulto"},"description":"Fran Agulto is a Developer Advocate at WP Engine. He is a lover of all things headless WordPress, Rock Climbing, and overall being stoked for people that love what they do and share that stoke with others! Follow me on Twitter for cool stoked headless WP!","url":"https:\/\/wpengine.com\/builders\/author\/francis-agultowpengine-com-2-2-2-2-2-2-2-2-2-2-2-3\/"}]}},"_links":{"self":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts\/32123","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/users\/20"}],"replies":[{"embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/comments?post=32123"}],"version-history":[{"count":0,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts\/32123\/revisions"}],"wp:attachment":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/media?parent=32123"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/categories?post=32123"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/tags?post=32123"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}