> ## 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 Signals MCP

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

## Overview

Commerce Signals MCP is an **extraction-only** MCP server. 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. Nothing is resolved: no affiliate offers, no product pages, no prices, no catalog lookups, and no country restrictions. You get the signal and decide what to do with it.

<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 Signals 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 Signals**
3. In the **Access Keys** card, click **Create Key**

<Frame caption="The Access Keys card on the Commerce Signals tab">
  <img src="https://mintcdn.com/chatads/8nqVsL9J0M7uuG3T/images/signals-access-keys.png?fit=max&auto=format&n=8nqVsL9J0M7uuG3T&q=85&s=556bcaf46ab1fed953879b6423665902" 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/signals-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-signals https://api.getchatads.com/tools/mcp/mcp --header "x-api-key: cak_your_access_key"
  ```

  ```json JSON config theme={null}
  {
    "mcpServers": {
      "chatads-signals": {
        "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.

## 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 mention named a specific brand (`true`) or is generic (`false`)                                                                                                                                            |
| `brand`      | string  | The matched brand label. Present only when `branded` is `true`                                                                                                                                                         |
| `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 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 `branded` comes back `true` with 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`.

## 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.
* Signals shares one usage pool with 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 the **Commerce Signals** tab.
* 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 Signals MCP takes a team access key (`cak_...`) - create one on the **Commerce Signals** 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).

### 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?"
