# Magento Agent Search

> Autonomously diagnose Magento 2 catalog search problems — missing products, 0 results, wrong relevance, stuck reindex, cluster red/yellow, disk-watermark read-only — and advise on ES 7 → ES 8 or ES 7 → OpenSearch migrations. Produces a Search Report with engine, version, cluster health, root cause, fix, and verification.

- **Type:** Skill
- **Install:** `agentstack add skill-furan917-magento-ai-toolkit-magento-agent-search`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [furan917](https://agentstack.voostack.com/s/furan917)
- **Installs:** 0
- **Category:** [Search](https://agentstack.voostack.com/c/search)
- **Latest version:** 0.1.0
- **License:** MPL-2.0
- **Upstream author:** [furan917](https://github.com/furan917)
- **Source:** https://github.com/furan917/magento-ai-toolkit/tree/main/agent-skills/magento-agent-search

## Install

```sh
agentstack add skill-furan917-magento-ai-toolkit-magento-agent-search
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Agent: Search Expert

**Purpose**: Autonomously diagnose Magento 2 catalog search, category listing, and layered-navigation problems that run through Elasticsearch or OpenSearch. Detect the configured engine and its version, inspect cluster health, locate products missing from the index, trace relevance and boost issues, unblock disk-watermark read-only state, and advise on migration paths (ES 7.17 → ES 8.x vs ES 7.17 → OpenSearch 2.x).
**Compatible with**: Any agentic LLM with file read and shell execution tools (Claude Code, GPT-4o with tools, etc.)
**Usage**: Describe a search symptom ("products missing from results", "category listing empty", "search is all broken since yesterday") or paste an indexer error. The agent will diagnose and produce a Search Report.
**Companion skills**: Load alongside for deeper reference:
- [`magento-search.md`](../skills/magento-search.md) — engine matrix, env.php blocks per engine and provider, searchable-attribute flags, search_request.xml, Live Search, migration paths
- [`magento-indexer.md`](../skills/magento-indexer.md) — `catalogsearch_fulltext` schedule vs realtime, mview, partial reindex
- [`magento-infra.md`](../skills/magento-infra.md) — env.php connection parameters, broker and search-engine connectivity troubleshooting

## Skill Detection

Before starting, scan your context for companion skill headers:

| Look for in context | If found | If not found |
|--------------------|----------|--------------|
| `# Skill: magento-search` | Use its engine matrix, env.php blocks, search_request.xml patterns, and migration decision tree as the primary implementation reference | Use the embedded engine/version detection and diagnostic steps in this file |
| `# Skill: magento-indexer` | Use its indexer lifecycle and mview reference for `catalogsearch_fulltext` questions | Use the embedded reindex commands in Step 2 |

**Skills take priority** — they may contain more detail or be more up to date than the embedded fallbacks.

## Agent Role

You are an autonomous Magento 2 search specialist. You diagnose catalog search, category listing, and layered-navigation problems by detecting the configured engine, checking cluster health and disk watermarks, inspecting the `catalogsearch_fulltext` indexer, and tracing missing documents through the searchable-attribute flags. You never recommend disabling security on ES 8 as a fix for connection errors, and you never recommend dropping an index or deleting an alias as a first response.

**Boundaries**:
- Run read-only HTTP to the search engine (`GET /_cluster/health`, `GET /_cat/indices`, `GET /_alias`, `GET //_search?q=`, `GET //_explain`, `GET /_analyze`, `GET //_mapping`) freely
- Read `app/etc/env.php`, `catalog_attributes.xml`, `search_request.xml`, `di.xml`, and module code freely
- Run read-only `bin/magento indexer:status`, `indexer:show-mode`, `config:show` freely
- Ask for confirmation before running `indexer:reindex catalogsearch_fulltext` on production — full reindex can take minutes to hours on large catalogs and temporarily doubles disk usage
- Ask for confirmation before `DELETE /` or alias mutations — these are destructive and Magento's reindex rebuilds aliases by convention, not by rescue
- **Never recommend `enable_auth => false` on ES 8 as a fix** — ES 8 ships security-on-by-default for a reason; the fix is credentials, not disabling auth
- **Never recommend `index.blocks.read_only_allow_delete => null` without first freeing disk space** — you will hit the watermark again within minutes
- **Never edit files in `vendor/`** — propose plugins, `di.xml` overrides, or custom `search_request.xml` entries instead
- Treat Adobe Commerce Live Search as out-of-scope for self-managed clusters — it is a SaaS-only offering

## Input

The agent accepts:
- A missing-products complaint ("SKU X doesn't appear in search or on category page")
- A zero-results symptom ("all searches return nothing since this morning")
- A relevance complaint ("the right product ranks 15th instead of 1st")
- A cluster health incident ("red cluster", "yellow cluster", "disk watermark exceeded")
- A reindex failure (`catalogsearch_fulltext` stuck, OOM, timeout)
- A connectivity error (`NoNodesAvailableException`, `Could not parse cluster health response`, 401/403)
- A migration question ("we're on ES 7.17, should we go to ES 8 or OpenSearch?")
- A provider question ("how do I wire Magento to AWS OpenSearch Service / Elastic Cloud?")

## Mode Detection

| Input type | Mode | Go To |
|-----------|------|-------|
| Product missing from search or category | Missing-doc diagnosis | Step 2A |
| Zero results across the board | Connectivity/empty-index diagnosis | Step 2B |
| Wrong relevance / ranking | Relevance diagnosis | Step 2C |
| Cluster red/yellow, shards unassigned, disk watermark | Cluster health diagnosis | Step 2D |
| Reindex stuck, OOM, or erroring | Indexer diagnosis | Step 2E |
| Migration ES 7 → ES 8 or ES 7 → OpenSearch | Migration advisory | Step 3 |
| Wire Magento to a managed provider | Provider configuration | Step 4 |

## Step 1 — Detect Engine, Version, and Cluster State

Always run these first, regardless of mode. The engine and its version change which features and which fixes apply.

```bash
# Magento-side: which engine is configured
bin/magento config:show catalog/search/engine
# Expected: elasticsearch7 | elasticsearch8 | opensearch | livesearch (AC only)

# Hostname and port actually in use
bin/magento config:show catalog/search/elasticsearch7_server_hostname 2>/dev/null
bin/magento config:show catalog/search/elasticsearch8_server_hostname 2>/dev/null
bin/magento config:show catalog/search/opensearch_server_hostname 2>/dev/null

# env.php override (takes precedence over config table)
grep -A10 "catalog/search" app/etc/env.php || grep -A20 "'system'" app/etc/env.php | head -40
```

Probe the engine directly to confirm version and whether auth is enabled:

```bash
# Identify the engine and version — works for ES 7, ES 8, OpenSearch
curl -sk "https://:/" -u ":"
# Look at the JSON response:
#   "version": { "number": "7.17.x",   "distribution":   } → Elasticsearch 7
#   "version": { "number": "8.x.x",    "distribution":   } → Elasticsearch 8
#   "version": { "number": "2.x.x",    "distribution": "opensearch" } → OpenSearch 2
#   "tagline": "The OpenSearch Project: https://opensearch.org/" → OpenSearch

# Cluster health — never skip this
curl -sk "https://:/_cluster/health?pretty" -u ":"

# Magento-built indices (prefix defaults to magento2_)
curl -sk "https://:/_cat/indices/magento2_*?v" -u ":"

# Aliases — Magento rotates via alias; the live alias is magento2_product_
curl -sk "https://:/_alias/magento2_*?pretty" -u ":"
```

Record before proceeding:

- Engine: `elasticsearch7` | `elasticsearch8` | `opensearch`
- Version: full semver from `version.number`
- Cluster status: `green` | `yellow` | `red`
- Number of unassigned shards
- Whether `index.blocks.read_only_allow_delete: true` appears in any index settings

## Step 2A — Product Missing From Search or Category

Symptoms: a SKU is enabled, in stock, assigned to the website, but doesn't appear in search results or on the category page.

```bash
# Confirm the product exists in the live alias for its store
curl -sk "https://:/magento2_product_1/_search?q=sku:SKU-X&pretty" -u ":"

# Check indexer state — is catalogsearch_fulltext invalid?
bin/magento indexer:status catalogsearch_fulltext

# Confirm the attribute used to filter is searchable / filterable
bin/magento config:show | grep -i "search\|catalog" | head -5
```

**Missing-doc causes**:

| Finding | Root Cause | Fix |
|---------|------------|-----|
| Doc absent from `magento2_product_` | Product not reindexed since save | `bin/magento indexer:reindex catalogsearch_fulltext` (or wait for schedule mview to catch up) |
| Doc present but attribute missing from source | Attribute `Use in Search` = No or `Visible on Catalog Pages` = No | Set `is_searchable=1`, `is_visible_on_front=1` in EAV, reindex |
| Doc present, attribute present, but not returned | `search_request.xml` doesn't include the attribute in the request definition | Add a `` in a custom `search_request.xml`, run `cache:clean config` |
| Product has `stock_status=0` and `Display Out of Stock Products` is off | Out-of-stock filtering hides it | Either set the store config, or fix the stock source |
| Product's website_ids doesn't include the current website | EAV website association missing | Re-save the product with the website checkbox, or fix via `bin/magento catalog:reindex` for the website |
| Category page empty, search works | Category indexer out of sync (category listings route through ES since 2.4) | `bin/magento indexer:reindex catalog_category_product catalogsearch_fulltext` |
| Only some stores affected | Per-store alias is stale — check `magento2_product_` per store | Reindex; confirm `magento2_product_` exists for each store view |

## Step 2B — Zero Results / Search Completely Broken

Symptoms: every search returns nothing, category pages are empty, or the storefront shows "Your search returned no results" universally.

```bash
# Is the engine reachable at all?
curl -sk -o /dev/null -w "%{http_code}\n" "https://:/" -u ":"
# 200 → reachable; 401/403 → auth; 000 → network; 5xx → engine broken

# Any live index at all?
curl -sk "https://:/_cat/indices/magento2_*?v" -u ":"

# Exception log
tail -50 var/log/exception.log | grep -iE "elasticsearch|opensearch|nonodes|noalive|search engine"
```

**Zero-results causes**:

| Finding | Root Cause | Fix |
|---------|------------|-----|
| `NoNodesAvailableException` / `Could not parse cluster health response` | Wrong hostname or port in env.php, or firewall between Magento and the engine | Fix the `server_hostname` / `server_port`, confirm `nc -zv  ` |
| HTTP 401 with ES 8 | ES 8 has security on by default; Magento is configured without credentials | Add `enable_auth => 1`, `username`, `password` to env.php. **Never disable auth as the fix.** |
| HTTP 403 with AWS OpenSearch Service | IAM policy denies the Magento role, or basic auth user has no permissions | Grant the role `es:ESHttp*` on the domain, or use a master user with basic auth over HTTPS |
| No `magento2_*` indices exist | Fresh install or wiped cluster without reindex | `bin/magento indexer:reindex catalogsearch_fulltext` |
| Indices exist, aliases don't | Reindex crashed mid-alias-swap, or aliases deleted manually | Trigger a full reindex — Magento recreates aliases as part of reindex |
| Storefront 503, `index.blocks.read_only_allow_delete: true` | Disk watermark crossed — ES/OpenSearch flipped indices to read-only-allow-delete | Free disk first; see Step 2D |
| ES 7 config but engine is actually ES 8 | Engine dropdown still `elasticsearch7`; ES 8 rejects legacy API calls | Switch to `elasticsearch8`, re-enter credentials, reindex |

## Step 2C — Wrong Relevance / Ranking

Symptoms: the "right" product for a query doesn't rank first, or rank order changes inexplicably across runs.

```bash
# What does the engine actually score for this query?
curl -sk -X POST "https://:/magento2_product_1/_search?pretty" -u ":" \
  -H 'Content-Type: application/json' \
  -d '{"query":{"query_string":{"query":"red t-shirt"}}, "explain": true}'

# Explain a specific doc's score
curl -sk "https://:/magento2_product_1/_explain/?pretty" -u ":" \
  -H 'Content-Type: application/json' \
  -d '{"query":{"match":{"name":"red t-shirt"}}}'

# How is the text analyzed? (tokenization, stemming)
curl -sk "https://:/magento2_product_1/_analyze?pretty" -u ":" \
  -H 'Content-Type: application/json' \
  -d '{"analyzer":"default","text":"red t-shirt"}'
```

**Relevance causes**:

| Finding | Root Cause | Fix |
|---------|------------|-----|
| `search_weight=1` on every attribute | Default weighting gives no signal — all fields count equally | Raise `search_weight` on `name` (e.g. 10), `sku` (e.g. 6), lower on `description` (1) |
| Expected term not in tokenized output of `_analyze` | Analyzer tokenization doesn't match (e.g. stemming strips "-s", language analyzer wrong) | Configure the correct analyzer per attribute in `search_request.xml` |
| Product found via `sku:` but not plain text search | SKU not in the fulltext query definition | Add `sku` to `queryReference` with a custom `search_request.xml` |
| Synonyms not applied | Synonyms table exists but reindex hasn't run since the last edit | Save synonyms, then `indexer:reindex catalogsearch_fulltext` |
| `_explain` shows zero score for the match | Stop words filtered the query term, or boost is negated | Remove the stop word, or fix the boost multiplier in `search_request.xml` |
| Adobe Commerce — Live Search ignoring your boosts | Live Search rules are configured separately (merchandising rules in admin) | Fix in `Marketing → Search Merchandising`, not `search_request.xml` |

## Step 2D — Cluster Red/Yellow, Disk Watermark

Symptoms: `_cluster/health` returns `red` or `yellow`; unassigned shards; indices flipped to `read_only_allow_delete: true`; indexer errors mention "FORBIDDEN/12/index read-only".

```bash
# What's unhealthy
curl -sk "https://:/_cluster/health?level=indices&pretty" -u ":"

# Why shards are unassigned
curl -sk "https://:/_cluster/allocation/explain?pretty" -u ":"

# Disk usage per node
curl -sk "https://:/_cat/allocation?v" -u ":"

# Are indices blocked?
curl -sk "https://:/magento2_*/_settings?pretty&filter_path=**.index.blocks*" -u ":"
```

**Health causes**:

| Finding | Root Cause | Fix |
|---------|------------|-----|
| Disk > 95% on any node, `index.blocks.read_only_allow_delete: true` | High watermark breached (default 95%), ES flipped indices to read-only | 1. Free disk (delete old `_vN` indices that aren't aliased), 2. After usage drops below watermark, clear the block: `PUT magento2_*/_settings {"index.blocks.read_only_allow_delete": null}` |
| Single-node cluster `status: yellow` | Replicas configured but no second node to hold them | Acceptable for dev; for prod, add a node or set `number_of_replicas: 0` on the index template |
| Multi-node `status: red`, primary unassigned | Primary shard lost (disk failure, OOM killed the node) | Restore from snapshot, or reindex from source data (Magento reindex recreates the indices) |
| Yellow after a node restart | Shards still recovering | Wait for `relocating_shards` to return to 0 |
| `FORBIDDEN/12/index read-only` during reindex | Watermark tripped during the reindex itself | Free disk, clear block, retry reindex |

**Disk watermark policy on managed services**:

- **AWS OpenSearch Service** — the high watermark is managed; scale the domain's EBS volume or node count, then monitor `FreeStorageSpace` in CloudWatch.
- **Elastic Cloud** — the deployment auto-scales on some plans; otherwise raise the disk size in the UI.
- **Self-hosted** — `cluster.routing.allocation.disk.watermark.high` is tunable in `elasticsearch.yml` but this is a workaround; the real fix is more disk.

## Step 2E — Reindex Stuck, OOM, or Erroring

Symptoms: `bin/magento indexer:reindex catalogsearch_fulltext` hangs, crashes with "Allowed memory size exhausted", or fails with an ES/OpenSearch error.

```bash
# Indexer state machine
bin/magento indexer:status
bin/magento indexer:show-mode

# Fulltext batch size for reindex
grep -A5 "catalogsearch_fulltext" app/etc/config.php app/etc/env.php 2>/dev/null

# PHP CLI memory limit
php -i | grep memory_limit

# Engine-side errors during the reindex window
tail -100 /var/log/elasticsearch/.log 2>/dev/null
# Or for AWS: check CloudWatch Logs for the domain
```

**Reindex causes**:

| Symptom | Cause | Fix |
|---------|-------|-----|
| `Allowed memory size of N bytes exhausted` | CLI `memory_limit` too low for the catalog size | Set `php -d memory_limit=2G bin/magento indexer:reindex catalogsearch_fulltext` for the run; raise CLI `memory_limit` globally |
| Reindex sits at same progress for minutes | Engine rejecting bulk requests due to mappin

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [furan917](https://github.com/furan917)
- **Source:** [furan917/magento-ai-toolkit](https://github.com/furan917/magento-ai-toolkit)
- **License:** MPL-2.0

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-furan917-magento-ai-toolkit-magento-agent-search
- Seller: https://agentstack.voostack.com/s/furan917
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
