> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getchatads.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Commerce Extract MCP

> Extract scored product and purchase-intent mentions from any text over one MCP tool

## Overview

Commerce Extract MCP is an **extraction-only** MCP tool. Give it any block of text - an AI reply, an article, a support thread - and it returns the product and purchase-intent mentions it found, each with a brand label when one was named, a confidence score, and the exact character offsets of the phrase.

It's the same extraction engine that powers the ChatAds widget, exposed on its own for builders who already have their own chat, agent, or commerce stack. `find_commerce_opportunities` resolves nothing: no affiliate offers, no product pages, no prices, no catalog lookups - you get the signal and decide what to do with it. When you want a phrase turned into a shoppable listing, that's the companion tool on the same server: [Commerce Match](/guides/commerce-match).

<Warning>
  **English only.** The extraction engine is built and tuned for English text. Non-English content generally returns few or no opportunities, so don't rely on it for other languages.
</Warning>

<Note>
  This is a **different server** from the [MCP Data Connector](/guides/mcp-data-connector). That one is read-only access to your own widget data with a `uk_` user key. This one takes a `cak_` team access key and analyzes text you send it.
</Note>

## Get Your Access Key

Commerce Extract MCP authenticates with a team **access key** (`cak_...`) in the `x-api-key` header.

1. Log into [app.getchatads.com](https://app.getchatads.com)
2. Open **Commerce Extract**
3. In the **Access Keys** card, click **Create Key**

<Frame caption="The Access Keys card on the Commerce Extract tab">
  <img src="https://mintcdn.com/chatads/2QQzjarJRU1Gaejo/images/extract-access-keys.png?fit=max&auto=format&n=2QQzjarJRU1Gaejo&q=85&s=119ab7a78cbb2d91b5918df875f14b6c" alt="Access Keys card in its empty state, with a Create Key button and a note that keys are stored using PGP encryption" width="2260" height="944" data-path="images/extract-access-keys.png" />
</Frame>

You can hold as many access keys as you have clients - one per MCP client is a good default, so you can revoke them independently. Widget keys (`cwk_...`) and user keys (`uk_...`) do not work here.

## Connect

In Claude Code it's one command. Most other clients (Claude Desktop, Cursor, Windsurf, etc.) take the JSON block - drop the `transport` field if your client rejects it:

<CodeGroup>
  ```bash Claude Code theme={null}
  claude mcp add --transport http chatads-commerce https://api.getchatads.com/tools/mcp/mcp --header "x-api-key: cak_your_access_key"
  ```

  ```json JSON config theme={null}
  {
    "mcpServers": {
      "chatads-commerce": {
        "url": "https://api.getchatads.com/tools/mcp/mcp",
        "transport": "http",
        "headers": {
          "x-api-key": "cak_your_access_key"
        }
      }
    }
  }
  ```
</CodeGroup>

It's a Streamable HTTP MCP server, so there's nothing to install locally and no Node or Python dependency - one URL in a config file. Restart your client and `find_commerce_opportunities` is available. The same server also carries [`find_product_matches`](/guides/commerce-match), so one entry gets you both tools.

## Tool

| Tool                          | Params                                                                                                                       | What it returns                                                       |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `find_commerce_opportunities` | `message` (required) - the text to scan; `metadata` (optional object, max 10KB) - arbitrary data echoed back on the response | An `opportunities` array of detected product mentions, plus a `count` |

Each opportunity:

| Field        | Type    | Description                                                                                                                                                                                                            |
| ------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `product`    | string  | The extracted product phrase, taken verbatim from your text                                                                                                                                                            |
| `branded`    | boolean | Whether the product phrase itself names the brand (or directly abuts it in your text)                                                                                                                                  |
| `brand`      | string  | The brand we can attach to this mention. Can be present when `branded` is `false` - see below                                                                                                                          |
| `confidence` | number  | Purchase-intent / extraction score from `0` to `1` - how strong a real, buy-relevant product mention this is. This is **not** a catalog match or relevance score; nothing is resolved in this mode                     |
| `span`       | array   | `[start, end]` character offsets of the phrase in the text you sent, for inline highlighting or linking. When a product is mentioned more than once, the span points to the first occurrence. Omitted when unavailable |

### Example

Calling the tool with:

```json theme={null}
{
  "message": "The Bose QuietComfort Ultra earbuds are great for commuting, but a good laptop stand matters more for posture."
}
```

returns:

```json theme={null}
{
  "data": {
    "opportunities": [
      {
        "product": "Bose QuietComfort Ultra earbuds",
        "branded": true,
        "brand": "Bose",
        "confidence": 0.87,
        "span": [4, 35]
      },
      {
        "product": "laptop stand",
        "branded": false,
        "confidence": 0.43,
        "span": [72, 84]
      }
    ],
    "count": 2
  },
  "error": null,
  "meta": {
    "request_id": "60556e82-ec0b-425b-8289-b95395db8943",
    "timestamp": "2026-07-23T14:42:01Z",
    "version": "1.0.0"
  }
}
```

The JSON above is the text response. Clients that support MCP structured tool output also receive the `data` payload directly as structured content - the bare `{"opportunities": [...], "count": n}` object, matching the tool's declared output schema. Anything you passed as `metadata` comes back on a top-level `metadata` field of the text response. Text with no detected purchase intent returns `"opportunities": []` with `"count": 0` - that's a success, not an error.

<Note>
  Opportunities come back **strongest first** - ordered by internal score, not by where they appear in the text - so the first item is the best pick, not the earliest mention. Each distinct product appears once; repeated mentions are collapsed into a single opportunity.
</Note>

## What `branded` and `brand` Mean

They answer different questions, so you can get a `brand` without `branded`.

* `branded` asks: does the phrase in `product` carry its own brand, or sit directly against it?
* `brand` asks: whose product is this?

A phrase that names its own brand sets both:

```text theme={null}
"The Bose QuietComfort Ultra earbuds are great for commuting."
{ "product": "Bose QuietComfort Ultra earbuds", "branded": true, "brand": "Bose" }
```

A generic phrase the text attributes to a brand sets only `brand`:

```text theme={null}
"You want a stand mixer like the KitchenAid, it lasts forever."
{ "product": "stand mixer", "branded": false, "brand": "KitchenAid" }
```

`product` is always verbatim from your text, so `span` keeps pointing at real characters - we never rewrite it to "KitchenAid stand mixer." If you're highlighting mentions inline, use `product` and `span`. If you're picking a link or a catalog, use `brand` and ignore `branded`.

A brand mentioned merely nearby sets neither:

```text theme={null}
"Just get the Ninja Creami. If you want cheaper, an immersion blender does 80% of the job."
{ "product": "immersion blender", "branded": false }
```

The immersion blender is the alternative to the Creami, not a Creami. Read a missing `brand` as "we have no brand claim for this phrase," not as "no brand appears nearby."

## What Gets Extracted

Some rules decide what's eligible before scoring runs, so you don't have to filter for them yourself:

* **Everything returned is at least two words.** Single-word mentions ("blender", "headphones") are dropped outright - too ambiguous to act on. A one-word product only comes back inside a longer phrase ("Vitamix A3500 blender").
* **Bare brand names are removed.** "Trader Joe's", "Nike", and "Costco" on their own return nothing. A brand survives only when it's attached to a product ("Patagonia Nano Puff jacket") - which is also when you get a `brand` label.
* **Non-product phrases are filtered.** Service names, vague trailing phrases, and mentions in negative context ("I wouldn't buy...") are dropped before scoring.

Together with the confidence floor below, that's why text full of shopping talk can still return `"count": 0`.

## Casing Is a Signal

Capitalization isn't cosmetic here - it feeds both the phrase boundaries we extract and the score the mention gets. Send text cased the way a person would actually write it:

* **Capitalize brand and product names.** A brand that isn't already in our brand data is recognized mostly by its casing pattern: two or more capitalized words in a row, or one capitalized word sitting next to a model number ("Kobo Clara BW", "Anker 737"). Written lowercase, the same phrase reads as generic prose and scores well below a branded mention - often under the confidence floor.
* **Don't capitalize words that aren't names.** Title Case prose ("Best Wireless Headphones For Running") can read as a brand and drag a generic phrase into branded territory.
* **A capital at the start of a sentence is ignored.** That's a sentence-case artifact, not a name, so a brand in that position needs a second capitalized word or a model number beside it.
* **ALL CAPS and all-lowercase text carry no casing signal at all.** Headline-cased, shouty, or lowercased input returns fewer opportunities at lower confidence. Normalize it before sending if you can.

## Confidence Scores

`confidence` is a proprietary score from `0` to `1`. Treat it as directionally accurate rather than a calibrated probability - it's built for ranking and thresholding mentions against each other, not for reading as "87% likely to convert."

Anything below roughly **0.40** is dropped before it reaches you, so you shouldn't need your own junk filter on top. An empty `opportunities` array means nothing on the page cleared that bar.

What moves the score:

* **Branded vs generic.** A mention naming a real brand ("Bose QuietComfort Ultra earbuds") scores well above a bare category noun ("earbuds").
* **Proximity to a purchase-intent signal.** Recommendation language near the mention ("I'd go with", "worth buying") lifts it; a passing reference in unrelated prose doesn't.
* **Location in the text.** Mentions early in the message, or set off structurally - a bullet, a numbered list, a bolded pick - score higher than ones buried mid-paragraph.
* **Length and specificity.** Multi-word phrases with real modifiers beat one-word generics, which are actively penalized.
* Plus a range of other signals in the scoring model.

Scores move as we tune the model, so if you gate on a threshold of your own, pick one, watch your outcomes, and revisit it. Don't hardcode a value and forget it.

## Limits

* **Message length:** 10,000 characters per call.
* **Requests:** the free tier includes 500 requests per month. Every call counts, including ones that return zero opportunities. [Get in touch](https://www.getchatads.com/contact) to raise your cap - tell us roughly what volume you're expecting.
* Commerce Extract shares one usage pool with [Commerce Match](/guides/commerce-match) and the rest of your team's API usage, and the same per-minute burst limit. Your current month's usage is shown at the top of both the **Commerce Extract** and **Commerce Match** tabs - same team-wide figure either way.
* Your team's blocked-keyword and language content rules still apply. A message that trips one returns an empty `opportunities` array rather than an error.

## Troubleshooting

### "This tool requires a cak\_ API key"

You connected with a user key (`uk_...`). Commerce Extract MCP takes a team access key (`cak_...`) - create one on the **Commerce Extract** or **Commerce Match** tab.

### "Message is required"

The `message` argument was missing, empty, or whitespace only. An empty message is a client error, not an empty-opportunities success.

### "Message exceeds max length"

The text was over 10,000 characters. Split it and call the tool once per chunk - the `span` offsets are relative to whatever you sent, so track your own chunk offsets if you're highlighting against the full document.

### "Extraction backend temporarily unavailable - please retry"

A `SERVICE_UNAVAILABLE` error means the extraction service didn't respond. It's deliberately surfaced as an error rather than silently returning zero opportunities, so a retry is safe and correct here.

### "Daily request limit exceeded" / "Monthly request limit exceeded"

You've used your plan's allowance. See [Limits](#limits).

### Text clearly mentions products but returns `"count": 0`

Check the casing first. Lowercased or ALL CAPS text strips the brand signal the engine leans on, and Title Case prose can push a generic phrase the wrong way - see [Casing Is a Signal](#casing-is-a-signal). After that, check [What Gets Extracted](#what-gets-extracted): single-word mentions and bare brand names are dropped by design, and anything under roughly 0.40 confidence is filtered out before you see it.

### Tool not appearing in your MCP client

1. Verify the URL is exactly: `https://api.getchatads.com/tools/mcp/mcp`
2. If your client's config uses a `transport` field, it should be `"http"`
3. Confirm the key in `x-api-key` starts with `cak_`
4. Restart your MCP client completely
5. Ask: "What tools do you have access to?"
