Controlling Super Speedy Search from an AI agent (MCP)
From version 5.57.0 Super Speedy Search exposes its whole tuning surface to AI assistants – Claude Code, Claude Desktop, Cursor, or anything else that speaks MCP. Eleven abilities let an assistant read your search configuration, run real searches, see exactly why each result ranked where it did, and change weights, synonyms and suggestion settings.
Connection is the same for every Super Speedy plugin – one setup covers the whole range, over AI Engine’s MCP endpoint or plain WordPress REST with an application password. The walkthrough lives in How to use Super Speedy MCP, together with the shared licence and install tools every plugin carries.
The point is the loop, not the individual tools. An assistant can run your customer’s actual search, look at which column earned each result its position, change one weight, run the search again and compare. That is a job nobody enjoys doing by hand across ten weights per post type, and it is the reason sss/test-query exists.
This article is written to be read by an LLM as well as by you. If you are an assistant connected to a site running this plugin, everything you need is below; call sss/describe-search-config first and it will hand you the same vocabulary as structured data.
Always call sss/describe-search-config before anything else. It returns the whole vocabulary as data – every weight key, the index columns, the synonym syntax, the recommended workflow and the plugin’s real limitations – so you tune against ground truth rather than guessing.
What you need
- WordPress 6.9 or newer. The abilities are built on the WordPress Abilities API, which is in core from 6.9. On older WordPress the plugin loads exactly as before and registers nothing.
- An Application Password for the account the assistant will use.
- No extra plugin on the site. The abilities are reachable over the standard REST API. (If you happen to have the MCP Adapter plugin installed, the plugin also registers a dedicated
/wp-json/mcp/super-speedy-searchserver, but it is not required.)
Setting up the agent account
Every ability is gated on a single capability, sss_manage_search. Any existing administrator has it automatically – there is no migration and no settings screen to visit.
For an AI agent, do not use an administrator account. The plugin creates a dedicated Search Manager role (sss_search_manager) that has read and sss_manage_search and nothing else. An account with that role can tune search and cannot touch anything else on the site.
wp user create search-agent agent@example.com --role=sss_search_manager
wp user application-password create search-agent mcp-bridge --porcelain
Keep the password the second command prints – it is shown once.
Connecting a client
The plugin ships a zero-dependency stdio bridge at tools/sss-abilities-mcp-bridge.mjs (Node 18+). It speaks MCP on your machine and proxies to the site’s REST API.
claude mcp add --transport stdio sss -- node /path/to/super-speedy-search/tools/sss-abilities-mcp-bridge.mjs
with these in the environment:
| Variable | Value |
|---|---|
WP_URL | https://example.com – the site root |
WP_USER | the agent’s username |
WP_APP_PASSWORD | the Application Password |
SSS_MCP_NAMESPACES | optional; defaults to sss |
New tools only appear when an MCP client session starts, so restart the client after adding it.
Tool names are dash-joined. The ability sss/test-query appears to the model as the tool sss-test-query. This article uses the ability names.
Calling the abilities without MCP
Every ability is also a plain REST endpoint, which is often the quickest way to check something or to script a one-off:
# read abilities are GET, with PHP-style query params
curl -u agent:APP_PASSWORD -G \
"https://example.com/wp-json/wp-abilities/v1/abilities/sss/test-query/run" \
--data-urlencode "input[q]=red dress" \
--data-urlencode "input[post_type]=product"
# write abilities are POST, with a JSON body
curl -u agent:APP_PASSWORD -X POST -H 'Content-Type: application/json' \
-d '{"input":{"post_type":"product","weights":{"tax":{"direction":"increase","factor":25,"enabled":true}}}}' \
"https://example.com/wp-json/wp-abilities/v1/abilities/sss/set-weights/run"
POSTing to a read ability returns 405 – that is the Abilities API enforcing the read/write split, not an error in the plugin.
The eleven abilities
Read (safe to call at any time)
| Ability | Inputs | What it gives you |
|---|---|---|
sss/describe-search-config | none | The whole vocabulary as data: every weight key with its meaning and shape, the index columns, synonym syntax, combination rules, which post types are searchable, the recommended workflow, and the plugin’s real limitations. Call this first. |
sss/test-query | q (required), post_type, limit, orderby | Runs a real front-end search and returns the ranked results with a per-result score breakdown. |
sss/get-index-status | none | table_built, rebuild_required, rebuild_running, last_build_finished, total_rows, rows_by_post_type, index_columns, table_columns, DB version and compatibility. |
sss/get-weights | post_type (optional) | The ranking weights for one post type, or for all of them. |
sss/list-synonyms | none | Every synonym group, raw and split into terms, each with the index you would pass to delete-synonym. |
sss/get-combination-settings | none | Taxonomy combination suggestion config, plus stored_rows – how many combinations are actually in the table right now. |
Write
| Ability | Inputs | Notes |
|---|---|---|
sss/set-weights | post_type, weights (both required) | Partial update – send only the keys you are changing. Unknown keys are rejected. Takes effect immediately. |
sss/upsert-synonyms | groups (required) | [["crimson","red"],["sofa","couch","settee"]]. A group sharing a term with an existing group is merged into it rather than duplicated. Immediate. |
sss/delete-synonym | index or term | Removes one group. |
sss/set-combination-settings | enabled, combinations, meta_axes, min_count, max_results, match_order, layout, segment_singles, segment_pairs, segment_triples | All optional; send what you are changing. combinations and meta_axes are full replacements. Needs a rebuild afterwards. |
sss/rebuild-index | scope, cursor, max_seconds, max_statements | Batched rebuild. See below. |
Nothing in this surface is destructive. There is no ability that deletes content, drops a table, or takes the site down; the worst an agent can do is misconfigure ranking, which sss/test-query will show it and another set-weights will undo.
Every write here is reversible. No ability deletes content, drops a table or touches anything outside the search configuration, so the worst outcome is a misordered result you can see in sss/test-query and fix with one more set-weights.
Reading a test-query result
This is the ability everything else is verified with, so it is worth understanding its output properly.
{
"query": "logitech mouse",
"sss_active": true,
"used_fulltext": true,
"found_posts": 2,
"result_count": 2,
"results": [
{
"rank": 1,
"ID": 1177,
"title": "Logitech G502 Hero Gaming Mouse",
"post_type": "product",
"url": "https://example.com/product/logitech-g502-hero-gaming-mouse",
"relevance": 938.7259,
"weights": {
"weight_tax": 735.7753,
"weight_title": 495.8967,
"weight_content": 11.8404,
"weight_base_match": 10.2135,
"weight_meta_column": 0,
"weight_excerpt": 0,
"weight_title_length": -315
},
"weights_sum": 938.7259,
"sku": "LOG-G502-HERO",
"price": 59.99
}
],
"query_analysis": {
"boolean_mode_expression": "+logitech* +mouse*",
"weighting_expression": "logitech* mouse*",
"tokens": ["logitech", "mouse"],
"short_token_like_queries": [],
"synonyms_fired": []
},
"db_errors": [],
"notes": []
}
Things to notice:
weights_sumequalsrelevance. The contributions add up to the score they explain. If they ever diverge, something is being scored that is not being reported – treat that as a bug worth reporting, not as normal noise.weight_title_lengthis negative. Longer titles are penalised, which is why “Logitech G502 Hero Gaming Mouse” outranks “Logitech MX Master 3 Wireless Mouse” here despite identical title and taxonomy scores. It is often the deciding factor between two otherwise equal results, and it surprises people.query_analysis.boolean_mode_expressionis what actually went to MySQL.+logitech* +mouse*means both terms are required and both are prefix-wildcarded.db_errorsis not decoration. If a query returns nothing, check it before concluding the catalogue has no match.sss_active: falsemeans Super Speedy Search did not handle this query and you are looking at core WordPress results. Do not report those as SSS rankings.
The weight_* columns
| Column | Contribution |
|---|---|
weight_base_match | The base score: a constant plus the raw FULLTEXT match across the whole index |
weight_title | Title match × the title factor |
weight_title_short_token | Prefix-LIKE match on the title for tokens under three characters |
weight_title_length | The title-length penalty. Always negative |
weight_content | Post content match |
weight_excerpt | Excerpt match |
weight_tax | Match against taxonomydescriptions – term names, plus term descriptions when that setting is on |
weight_meta_column | Match against the concatenated whitelisted meta values (SKU, GTIN, brand …) |
weight_post_date_age | Age-based adjustment |
weight_meta_match_<key> | One per metamatches rule (a _total when several post types are ranked together) |
weight_meta_value_<key> | One per metavalues rule |
weight_meta_date_<key> | One per metadates rule |
weight_taxmatch_<tax>_<slug> | One per taxmatches rule |
weight_post_type_promotion | The flat per-post-type constant, in multi-post-type searches |
weight_meta_key_post_id | A fixed boost for rows matched via the meta-key search path |
A contribution only appears when the corresponding weight is enabled and the query could produce a score for it, so an absent column is itself information: no weight_excerpt means the excerpt weight is off or the excerpt index is missing.
The weight vocabulary
Weights are per post type. sss/describe-search-config returns all of this with descriptions; the shapes are:
Scalar weights – title, content, excerpt, tax, agehours:
{ "direction": "increase", "factor": 50, "enabled": true }
direction is increase or decrease and nothing else. factor is a number.
post_type_promotion – { "enabled": true, "factor": 100 }. A flat constant added when several post types are ranked in one list. No effect on single-post-type searches.
Rule lists – each is a full replacement array:
| Key | Row shape | Rebuild needed? |
|---|---|---|
metamatches | {meta_key, meta_value, comparison, direction, factor} | Yes |
metavalues | {meta_key, direction, factor} | Yes |
metadates | {meta_key, direction, factor} | Yes |
taxmatches | {taxonomy, term_slug, comparison, direction, factor} – comparison is matches or not_matches | No |
Meta-based weights read dedicated index columns that only exist after a rebuild. taxmatches reads a column that is always there, so it works immediately. sss/set-weights tells you which case you are in via its notes.
The rebuild loop
sss/rebuild-index never blocks for the whole rebuild – on a large catalogue that would time out and leave the index half-built. It works through a list of SQL statements against a cursor:
- Call with
cursor: 0and ascope. - Read
next_cursoranddonefrom the response. - While
doneis false, call again with the returnednext_cursor. - When
doneis true, callsss/get-index-statusto confirm the row counts, thensss/test-queryto confirm search still behaves.
Each call runs statements until its time budget is spent – 20 seconds by default, tunable with max_seconds (5 – 60). max_statements caps the statements per call instead, which is what to reach for if a rebuild keeps timing out and you want small predictable steps.
Scopes: all, search, suggestions, taxonomy_suggestions. They match the wp sss rebuild subcommands.
The statement list is fingerprinted when the loop starts and checked on every resume. If the settings change mid-rebuild the resume is refused with sss_rebuild_changed rather than running half of one plan and half of another – start again from cursor: 0. Restarting is always safe; the rebuild is idempotent.
When a rebuild is actually needed: after changing which columns are indexed, after adding meta-based weights, after changing combination settings, and after a bulk content import. Not after changing scalar weights, taxmatches, or synonyms – those are live immediately. An assistant that rebuilds after every weight tweak is wasting a lot of your database’s time.
Scalar weights, taxmatches and synonyms take effect the instant you set them – no rebuild. Only meta-based weights, index columns and combination settings need a rebuild. Skip the rebuild when you do not need one and the tune-test loop stays fast.
Recipes
Tune a search that returns the wrong order
sss/describe-search-config
sss/get-weights { "post_type": "product" }
sss/test-query { "q": "red dress", "post_type": "product", "limit": 10 } ← baseline
→ look at which weight_* dominates the results that should NOT be at the top
sss/set-weights { "post_type": "product", "weights": { "content": { "direction": "increase", "factor": 2, "enabled": true } } }
sss/test-query { "q": "red dress", "post_type": "product", "limit": 10 } ← compare
Change one thing per iteration. With ten weights in play, two changes at once tell you nothing about either.
Fix a vocabulary mismatch – the customer says “crimson”, the catalogue says “red”:
sss/test-query { "q": "crimson" } → 0 results
sss/upsert-synonyms { "groups": [["crimson","red"]] }
sss/test-query { "q": "crimson" } → the red products
Stop the suggestions dropdown filling with dead ends on a large catalogue:
sss/get-combination-settings → min_count is 1
sss/set-combination-settings { "min_count": 5 }
sss/rebuild-index { "scope": "taxonomy_suggestions", "cursor": 0 } → loop until done
Prepare a fresh or staging site with no SSH access:
sss/get-index-status → table_built false
sss/rebuild-index { "scope": "all", "cursor": 0 } → loop until done
sss/get-index-status → row counts per post type
sss/test-query { "q": "<something you know exists>" }
What this plugin does not do
Be straight with people about these. An assistant that promises them will produce a support ticket.
- There is no fuzzy matching and no typo tolerance. Matching is MySQL FULLTEXT boolean mode plus prefix wildcards.
dresmatches “dress” because of the trailing wildcard;dreesmatches nothing at all. For known misspellings, add a synonym group – that is the supported fix. - Tokens shorter than three characters cannot use the FULLTEXT index and fall back to a prefix LIKE. They appear in
query_analysis.short_token_like_queries. sss/test-queryruns in a front-end context, so hidden and out-of-catalogue products are excluded exactly as a shopper would see. It is not an admin search.- If site search override is switched off,
sss/test-queryreturns the errorsss_search_override_disabledrather than core WordPress results. This is deliberate: results that did not come from Super Speedy Search must never be reported as if they had. - A synonym match currently flattens ranking. When every typed word is consumed by a synonym group, the weighted contributions drop out and results are ordered by base match, meta rules and the title-length penalty only. The right products come back, but not in the order the equivalent non-synonym search would give.
Error codes
Every rejection returns a stable code so an assistant can correct itself rather than guess.
| Code | Meaning |
|---|---|
ability_invalid_permissions | The account lacks sss_manage_search |
ability_invalid_input | Input failed schema validation – an unknown property, or a value outside an enum |
sss_empty_query | test-query was called with an empty q |
sss_search_override_disabled | SSS is not overriding site search, so there is nothing honest to return |
sss_unknown_post_type | That post type is not searchable; the message lists the ones that are |
sss_unknown_weight_key | The message lists every valid weight key |
sss_bad_weight_shape | A weight was sent as the wrong type, or a rule row is missing a field |
sss_bad_direction | direction must be increase or decrease |
sss_bad_comparison | A taxmatches comparison must be matches or not_matches |
sss_missing_post_type, sss_missing_weights, sss_missing_groups, sss_missing_selector | A required input was absent |
sss_group_too_small | A synonym group needs at least two distinct terms |
sss_bad_term, sss_bad_group | Malformed synonym input – commas separate terms, so a term cannot contain one |
sss_not_found | No synonym group at that index, or containing that term |
sss_bad_combinations, sss_bad_meta_axes, sss_unknown_taxonomy | Invalid combination configuration |
sss_nothing_to_change | set-combination-settings was called with no settings |
sss_bad_scope, sss_no_statements, sss_no_builder | Rebuild could not start |
sss_rebuild_busy | A previous rebuild is still rolling back; wait and retry from cursor: 0 |
sss_rebuild_lost | Resume state expired – start again from cursor: 0 |
sss_rebuild_changed | Settings changed mid-rebuild – start again from cursor: 0 |
sss_rebuild_failed | A statement errored; the message names the statement number and the database error |

Comments