Super Speedy Search

Controlling Super Speedy Search from an AI agent (MCP)

Published · Last updated

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.

Best practice

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-search server, 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:

VariableValue
WP_URLhttps://example.com – the site root
WP_USERthe agent’s username
WP_APP_PASSWORDthe Application Password
SSS_MCP_NAMESPACESoptional; 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)

AbilityInputsWhat it gives you
sss/describe-search-confignoneThe 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-queryq (required), post_type, limit, orderbyRuns a real front-end search and returns the ranked results with a per-result score breakdown.
sss/get-index-statusnonetable_built, rebuild_required, rebuild_running, last_build_finished, total_rows, rows_by_post_type, index_columns, table_columns, DB version and compatibility.
sss/get-weightspost_type (optional)The ranking weights for one post type, or for all of them.
sss/list-synonymsnoneEvery synonym group, raw and split into terms, each with the index you would pass to delete-synonym.
sss/get-combination-settingsnoneTaxonomy combination suggestion config, plus stored_rows – how many combinations are actually in the table right now.

Write

AbilityInputsNotes
sss/set-weightspost_type, weights (both required)Partial update – send only the keys you are changing. Unknown keys are rejected. Takes effect immediately.
sss/upsert-synonymsgroups (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-synonymindex or termRemoves one group.
sss/set-combination-settingsenabled, combinations, meta_axes, min_count, max_results, match_order, layout, segment_singles, segment_pairs, segment_triplesAll optional; send what you are changing. combinations and meta_axes are full replacements. Needs a rebuild afterwards.
sss/rebuild-indexscope, cursor, max_seconds, max_statementsBatched 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.

Did you know?

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_sum equals relevance. 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_length is 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_expression is what actually went to MySQL. +logitech* +mouse* means both terms are required and both are prefix-wildcarded.
  • db_errors is not decoration. If a query returns nothing, check it before concluding the catalogue has no match.
  • sss_active: false means 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

ColumnContribution
weight_base_matchThe base score: a constant plus the raw FULLTEXT match across the whole index
weight_titleTitle match × the title factor
weight_title_short_tokenPrefix-LIKE match on the title for tokens under three characters
weight_title_lengthThe title-length penalty. Always negative
weight_contentPost content match
weight_excerptExcerpt match
weight_taxMatch against taxonomydescriptions – term names, plus term descriptions when that setting is on
weight_meta_columnMatch against the concatenated whitelisted meta values (SKU, GTIN, brand …)
weight_post_date_ageAge-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_promotionThe flat per-post-type constant, in multi-post-type searches
weight_meta_key_post_idA 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:

KeyRow shapeRebuild 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_matchesNo

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:

  1. Call with cursor: 0 and a scope.
  2. Read next_cursor and done from the response.
  3. While done is false, call again with the returned next_cursor.
  4. When done is true, call sss/get-index-status to confirm the row counts, then sss/test-query to 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.

Performance Tip

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. dres matches “dress” because of the trailing wildcard; drees matches 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-query runs 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-query returns the error sss_search_override_disabled rather 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.

CodeMeaning
ability_invalid_permissionsThe account lacks sss_manage_search
ability_invalid_inputInput failed schema validation – an unknown property, or a value outside an enum
sss_empty_querytest-query was called with an empty q
sss_search_override_disabledSSS is not overriding site search, so there is nothing honest to return
sss_unknown_post_typeThat post type is not searchable; the message lists the ones that are
sss_unknown_weight_keyThe message lists every valid weight key
sss_bad_weight_shapeA weight was sent as the wrong type, or a rule row is missing a field
sss_bad_directiondirection must be increase or decrease
sss_bad_comparisonA taxmatches comparison must be matches or not_matches
sss_missing_post_type, sss_missing_weights, sss_missing_groups, sss_missing_selectorA required input was absent
sss_group_too_smallA synonym group needs at least two distinct terms
sss_bad_term, sss_bad_groupMalformed synonym input – commas separate terms, so a term cannot contain one
sss_not_foundNo synonym group at that index, or containing that term
sss_bad_combinations, sss_bad_meta_axes, sss_unknown_taxonomyInvalid combination configuration
sss_nothing_to_changeset-combination-settings was called with no settings
sss_bad_scope, sss_no_statements, sss_no_builderRebuild could not start
sss_rebuild_busyA previous rebuild is still rolling back; wait and retry from cursor: 0
sss_rebuild_lostResume state expired – start again from cursor: 0
sss_rebuild_changedSettings changed mid-rebuild – start again from cursor: 0
sss_rebuild_failedA statement errored; the message names the statement number and the database error

Further reading

the same four rebuild scopes as `sss/rebuild-index`, run over SSH instead. Worth using when you have shell access and no reason to go through an agent.
what `sss/set-combination-settings` is actually configuring, including what the axes and the minimum post count do to the dropdown.
read this if `sss/test-query` returns nothing and `db_errors` is populated immediately after a rebuild. It is a database quirk on a freshly rebuilt fulltext index, not a configuration problem, and it clears on its own.
the same per-row weight breakdown is available to a human in the browser via `?showweights=1` on a search URL, as an administrator.

Comments

▲
▼
1/1