Appearance
Search tools reference β
The Product (commerce) MCP server exposes 10 read-only tools your AI sales agent uses to search, filter, and fetch a merchant's catalog. Inside MyChatBot these run automatically whenever the agent answers a product question β you don't call them by hand. You can also point an external MCP client (Claude Code, Cursor, or any MCP host) at the same tools to build your own sales agent on top of your catalog.
This page is the cheat sheet: every tool, what it does, what it accepts, and β crucially β what each filter operator means. All ten operators are enforced on every filter-accepting tool, including semantic_product_search; the remaining skill is picking the operator that matches the intent. Reach for it whenever you're writing a search prompt or debugging why a filter didn't match what you expected.
All 10 tools live behind one MCP server URL, scoped to a single account + product integration:
https://product.mychatbot.app/mcp/{account_id}/{integration_id}/streamIn the app β get the connect command
You don't assemble the URL by hand. Open the Sales Platform's Knowledge Base (https://app.mychatbot.app/knowledge-base) or Agents β Connectors (https://app.mychatbot.app/agents/connectors), find your product integration card, and choose Connect AI tools. MyChatBot fills in the correct account + integration and hands you a copy-paste setup command for Claude Code plus a Cursor config. See Product (commerce) MCP for the full walkthrough.
Cheat sheet β
All 10 tools at a glance β
| Tool | What it does | Key inputs | Filter operators |
|---|---|---|---|
semantic_product_search | Natural-language + visual catalog search, ranked by meaning | query and/or image_url; optional limit=20, filters[] | all ten β eq, ne, gt, gte, lt, lte, in, contains, startswith, endswith |
filter_category_products | Deterministic browse + filtering inside one known category | category_id; optional filters[], limit=100 | all ten |
get_all_categories | Returns the full category tree | β | β |
get_product_details | Full record for one product | product_id | β |
get_product_variants | A product's sizes / colors / other variants | product_id | β |
find_similar_products | "More like this" / cross-sell from a product | product_id; optional limit=10 | β |
get_category_attributes | Which attributes a category has | category_id | β |
get_category_attribute_values | An attribute's type + its common values | category_id, attribute_name | β |
get_available_filters | Facet values + product counts per value (for faceted UIs) | category_id; optional filters[], facet_attributes[] | all ten |
get_products_by_ids | Exact batch fetch by known IDs | product_ids[] | β |
semantic_product_searchhas no required input β pass at least aqueryor animage_url.
What each operator means β
All three filter-accepting tools enforce every operator with the same semantics β a filter is never silently ignored, on semantic_product_search included. Pick by intent:
| Operator | Meaning | Reach for it when |
|---|---|---|
eq | Exact value match, or an exact element of a list-shaped attribute (native list or comma-separated string; element match is case-insensitive) | Categorical facets (brand, color) and list membership β coverage lists like cities within radius |
in | OR of eq across the listed values | "Any of these exact values/elements" |
ne | The exact negation of eq β excludes the exact value and any case-insensitive list element | "Everything except X" |
gt, gte, lt, lte | Numeric comparison whenever both sides read as numbers β a JSON number and a numeric string like "30" behave identically, even on attributes stored as text. A range means the attribute exists and compares: products lacking the attribute, and values that don't read as numbers against a numeric bound, are excluded | Price and other numeric ranges β the stored values must read as plain numbers (decorated values like "1 200 Π³ΡΠ½" don't match) |
contains | Case-insensitive literal substring in a string field β matches inside words | Partial names and other genuine substring lookups; never list membership |
startswith, endswith | Case-sensitive literal prefix/suffix of the whole stored string (comma lists are not split; on a native list any element may match) | Code/name prefixes where case is known |
Match the operator to the attribute's shape
contains matches inside words ("Neath" matches Rosneath) β use eq/in for list membership. startswith/endswith are case-sensitive and test the whole stored string, not comma-separated elements β use contains when case or position is uncertain. Range operators compare numerically whenever the stored values read as numbers β even on attributes indexed as text β but a decorated value like "1 200 Π³ΡΠ½" has no numeric reading and cannot be compared; check what the values look like with get_category_attribute_values first.
Which tool for which job β
| Job | Tool |
|---|---|
| Natural-language / conversational lookup ("gift for dry skin") | semantic_product_search |
| Visual search ("find this dress") | semantic_product_search with image_url |
| Rank by meaning but constrain by attributes | semantic_product_search with filters[] |
| Match a partial product name or other string fragment | semantic_product_search with contains, or filter_category_products when the category is known |
| Free-text query + price / numeric range | semantic_product_search with gte/lte filters (see the example below) |
| Deterministic browse + range filtering inside one known category | filter_category_products |
| List the catalog's category tree | get_all_categories |
| Full record for one product (agent already has the ID) | get_product_details |
| Sizes / colors / other variants of a product | get_product_variants |
| "More like this" / cross-sell from a product | find_similar_products |
| Discover what attributes a category has | get_category_attributes |
| Discover a specific attribute's type + common values | get_category_attribute_values |
| Build a faceted UI / count how many products per value | get_available_filters |
| Exact batch lookup by known IDs (e.g. SKUs already resolved) | get_products_by_ids |
Defaults β
| Input | Applies to | Default |
|---|---|---|
limit | semantic_product_search | 20 (max 60) |
limit | filter_category_products | 100 |
limit | find_similar_products | 10 |
enable_reranking | semantic_product_search | true |
truncate_description | semantic_product_search | true |
Passing filters β
Every entry in filters[] (for semantic_product_search, filter_category_products, and get_available_filters) has three parts:
json
{ "attribute": "price", "operator": "gte", "value": 500 }- attribute β the attribute name to constrain on (e.g.
price,vendor,stickers). The reserved attributenametargets the product's top-level name. - operator β
eq, ne, gt, gte, lt, lte, in, contains, startswith, endswith. Every operator carries the same semantics on all three filter-accepting tools (see What each operator means above). - value β a scalar for most operators; a list when the operator is
in; a nonblank string when it iscontains. For range operators a JSON number and a numeric string (50or"50") behave identically.
Multiple filters are AND-composed β every filter must match. In semantic_product_search they apply across the whole ranked search.
contains is case-insensitive, applies only to string fields, and treats its value as a literal substring β it matches inside words, so contains "Neath" matches Rosneath. For example, name contains ASHEN matches The Ashen Evening Dress. Characters such as * and ? are ordinary text, not wildcard syntax.
eq matches the exact attribute value, or an exact element of a list-shaped attribute β a native list field such as stickers, or a comma-separated string such as a cities within radius coverage list. Full-string equality is exact; the element match is case-insensitive and ignores surrounding whitespace, so eq "rosneath" matches a list containing Rosneath while eq "Neath" does not (Neath is not an element). in behaves as OR of eq across its values β "has any of these exact values/elements", with the same case-insensitive element matching.
ne is the exact negation of eq: it excludes a product when the stored value equals the filter value or contains it as an exact (case-insensitive) list element β so country ne "great britain" excludes a stored Great Britain.
startswith / endswith are case-sensitive literal prefix/suffix tests against the whole stored string. A comma-separated value is not split into elements (startswith "Alexandria" does not match Airdrie, Alexandria, β¦); on a native list attribute such as stickers, any element may carry the prefix/suffix. When case or position is uncertain, prefer contains.
Range operators (gt, gte, lt, lte) compare numerically whenever both sides read as numbers β a JSON number and a numeric string like "150" behave identically, and this holds even when the attribute is stored as text (a radius attribute holding "50" range-filters numerically). Genuinely textual pairs compare lexicographically. A range means the attribute exists and compares: products that lack the attribute do not match, and neither do values with no numeric reading against a numeric bound (a decorated value such as "1 200 Π³ΡΠ½" is excluded, not passed through). On a native list attribute, any element may satisfy the comparison.
Membership is eq's job, never contains's
contains is a substring match: on a coverage list containing Rosneath, contains "Neath" wrongly matches. Use eq (or in) to test list membership; keep contains for genuine partial-text lookups.
Range and prefix filters are enforced on semantic_product_search too
Every operator is applied as a hard constraint on the ranked results β a filter is never silently dropped. One nuance: only eq/in/contains also guide candidate discovery on a whole-catalog search; the other operators purely constrain what comes back. So keep the descriptive intent in query, and check with get_category_attribute_values that a range-filtered attribute holds values that read as plain numbers.
Example tool calls β
These show how to drive the tools over MCP. Each is a tools/call with a tool name and its arguments.
Semantic search with an attribute filter β
Rank by meaning, but only among products carrying a specific sticker:
json
{
"name": "semantic_product_search",
"arguments": {
"query": "gift for dry skin",
"limit": 20,
"filters": [
{ "attribute": "stickers", "operator": "eq", "value": "ΠΠΎΠ΄Π°ΡΡΠ½ΠΎΠΊ" }
]
}
}stickers is a string-list attribute, so an eq matches any single element in the list. This is the sweet spot for semantic_product_search filters: eq/in on a string attribute.
Partial product-name filter β
Match a product even when the shopper supplies only part of its name:
json
{
"name": "semantic_product_search",
"arguments": {
"query": "evening dress",
"filters": [
{ "attribute": "name", "operator": "contains", "value": "ASHEN" }
]
}
}This match is case-insensitive and searches the top-level product name. The same filter can narrow filter_category_products or get_available_filters when you also supply their required category_id.
Free-text query with a price range β
Range filters are enforced directly on semantic_product_search β one call combines free-text ranking with a hard numeric constraint:
json
{
"name": "semantic_product_search",
"arguments": {
"query": "waterproof hiking boots",
"limit": 20,
"filters": [
{ "attribute": "price", "operator": "gte", "value": 80 },
{ "attribute": "price", "operator": "lte", "value": 200 }
]
}
}The bounds may be JSON numbers or numeric strings (80 and "80" behave identically), and the comparison is numeric even when the attribute is stored as text β the only hard requirement is that the stored values read as plain numbers; a feed that shipped price as decorated text ("1 200 Π³ΡΠ½") cannot be range-compared (get_category_attribute_values shows what the values look like). When you'd rather browse a whole category deterministically than rank by meaning, the classic pattern still applies: resolve a category_id from the returned categories[], then run the same range filters through filter_category_products.
Visual search β
json
{
"name": "semantic_product_search",
"arguments": {
"image_url": "https://example.com/customer-photo.jpg",
"limit": 10
}
}Multi-value in filter β
json
{
"name": "semantic_product_search",
"arguments": {
"query": "running shoes",
"filters": [
{ "attribute": "brand", "operator": "in", "value": ["Nike", "Adidas", "Asics"] }
]
}
}Deterministic category browse with a price range β
json
{
"name": "filter_category_products",
"arguments": {
"category_id": "shoes-running",
"filters": [
{ "attribute": "price", "operator": "gte", "value": 50 },
{ "attribute": "price", "operator": "lte", "value": 150 }
],
"limit": 100
}
}Introspect a category before filtering β
json
{ "name": "get_category_attributes", "arguments": { "category_id": "shoes-running" } }
{ "name": "get_category_attribute_values", "arguments": { "category_id": "shoes-running", "attribute_name": "brand" } }
{ "name": "get_available_filters", "arguments": { "category_id": "shoes-running", "facet_attributes": ["brand", "color"] } }Exact batch fetch by ID β
json
{
"name": "get_products_by_ids",
"arguments": { "product_ids": ["SKU-1001", "SKU-1002", "SKU-1003"] }
}Best practices β
Do
- Start conversational queries with
semantic_product_search; it understands the meaning of the query and reranks the best matches to the top. - Pick operators by intent:
eq/infor exact values and list membership (native lists and comma-separated attributes such as coverage lists),containsfor partial string matching,gt/gte/lt/ltefor numeric ranges,neto exclude a value/element,startswith/endswithfor case-sensitive prefixes/suffixes. - Discover valid attribute names/values with
get_category_attributesandget_category_attribute_valuesbefore sending filters β don't guess attribute names, and confirm range-filtered attributes hold values that read as plain numbers. - Use
filter_category_productswhen the category is known and you want a deterministic category browse rather than ranked recall. - Use
get_products_by_ids(orget_product_details) when the agent already resolved exact IDs β skip ranking entirely. - Keep
enable_reranking=truefor natural-language queries; it materially improves ordering.
Don't
- Don't use
containson numeric or boolean fields β it requires a nonblank string field and value β and never use it for list membership: it matches inside words (contains "Neath"matchesRosneath); useeq/ininstead. - Don't treat
containsas wildcard syntax β*and?are matched literally (the same holds forstartswith/endswithvalues). - Don't reach for
startswith/endswithwhen case or position is uncertain β they are case-sensitive and test the whole stored string;containsis the forgiving option. - Don't range-filter attributes whose values don't read as plain numbers (
"1 200 Π³ΡΠ½") β those pairs can't be compared numerically. Numeric bounds may be numbers or numeric strings; both behave identically. - Don't request a
limitabove 60 forsemantic_product_search. - Don't send
filtersto tools that don't accept them (find_similar_products,get_product_details,get_all_categories, etc.).
Reranking is auto-skipped for code / URL queries
Even with enable_reranking=true, reranking is automatically skipped when the query looks like a SKU/barcode or a URL β MyChatBot tunes exactly when it kicks in. For exact SKU-style lookups, prefer get_products_by_ids, or set enable_reranking=false.
truncate_description trims payloads
truncate_description defaults to true, so semantic_product_search results carry shortened descriptions. Set it to false only when the agent needs the full text. Very long descriptions are shortened when indexed regardless.
Test it β
- Call
get_all_categories, pick acategory_id, then chainget_category_attributesβget_category_attribute_valuesto confirm which attributes and values are actually filterable for that catalog β and whether a range-target likepriceholds values that read as plain numbers. - Run a known-good query through
semantic_product_searchand assert the expected product IDs appear. - Verify a price range on
semantic_product_searchdirectly (products outside the bound must not appear), and cross-check the same filter throughfilter_category_productsβ both enforce the same contract. - If an attribute filter silently returns nothing, check the operator/attribute pairing first (numeric bound on a string attribute? case-sensitive prefix?); otherwise the integration's searchable attributes may be out of date β re-index by removing and re-adding the product integration, or ask support to run a full re-index.