Connect an MCP client to your dashboard
Point your own Claude or any MCP client at your AISA conversations, labels, reports and agent versions, authenticated as your organization. Nine read-only tools and three ready-made prompts.
You can point your own MCP client (your own Claude, Claude Desktop, or any MCP client) at your AISA dashboard data, authenticated as your organization via WorkOS. This is a different server from the docs MCP server: that one is public and unauthenticated and only knows about documentation pages; this one is org-scoped and reads your actual conversations.
The server is read-only. It finds and returns conversations, their labels, the dashboard's reports, label counts, your sites and the configuration of your agent versions. It doesn't analyze anything itself: your client's model reads what the tools return and does the analysis.
It only knows what the assistant did: the conversations it had, the ratings and labels they got, and the widget events around them. It has no site traffic, no revenue or order value, and no market or competitor data. See What the server doesn't give you.
Connect
Open the connect card
Go to Settings in your dashboard. You'll see a Connect an MCP client card with a URL.
Copy the URL
Copy the URL from the card. It's the same for every conversation and every client: the server figures out which organization you belong to from how you authenticate, not from anything in the URL.
Add it to your MCP client
Add it as a remote server in your client's configuration. For Claude Desktop or Claude.ai's custom connectors, this is normally a "Connect" or "Add custom connector" step where you paste the URL:
{
"mcpServers": {
"iadvize-dashboard": {
"url": "https://www.iadvize.ninja/api/mcp"
}
}
}Authorize the connection
Your client detects that this server requires authorization and redirects you to sign in with your iAdvize account (the same WorkOS login the dashboard uses), then asks you to grant it access. Once you approve, the client holds a short-lived, scoped token. Nothing is pasted or stored by hand.
Ask it a question
Ask your client something like "which conversations about sizing went unresolved last week?". It picks the tools it needs. See A worked example for the calls it makes.
Tools
The server has nine tools. Every one only reads, and none takes an organization argument: the organization is the one you authorized.
| Tool | Does what |
|---|---|
list_sites | List your sites (id, name, URL, platform), each with its agents (id, name, type) and the number and id (agtv_...) of the agent's live version, or null when it has none. |
list_label_vocabulary | List every label of the conversation taxonomy with its block, kind, allowed values and an English display name for each, plus the derived tokens. These are the only tokens search_conversations accepts. |
search_conversations | Find conversations that match a filter, newest first, as compact items. Searches every site unless you pass siteId. |
get_conversation | Read one conversation by id: its full transcript and its metadata. |
get_conversations | Read up to 20 conversations by id in one call: metadata, a compact transcript and the label record of each. |
get_conversation_labels | Read the label record of one conversation: its status and, once labeled, the resolution, review reason, scores and every answer with the ids of the messages that justify it. |
get_report | Read one of the dashboard's reports for one site over a range of UTC days, optionally next to the previous period: conversations, quality, engagement or starter questions. |
get_label_breakdown | Count the conversations per value of one label, such as the transfer reasons or the resolution outcomes, over a filter, optionally next to the previous period. |
get_agent_version | Read what one agent version runs with (instructions, enabled tools, engine) or what changed between two versions. Needs the Agents: View or Agents: Edit permission. |
Every result carries asOf, the time the server read the data, in UTC. The server tells the assistant to resolve "last week" or "yesterday" against it. Every conversation names its site and carries a dashboard link, so you can open it from the answer. The text of a conversation also gives the version of the agent that handled it with the version's id, and list_sites gives the id of each live version, so a client that reads only text can pass either to get_agent_version.
Find conversations
search_conversations takes the same filters as the REST conversation list, written as JSON instead of query parameters, and returns the same conversations for the same filters.
| Field | Does what |
|---|---|
siteId | Restrict the search to one site. Omit it to search every site of your organization. |
from, to | Creation time, ISO 8601 with an offset. from is inclusive, to is exclusive. |
channel | web, hosted or playground. |
agentId | An agent id from list_sites. |
language | The language the conversation was held in. |
feedback | positive or negative: at least one shopper rating of that kind. |
keywords | Terms that must all appear in the conversation title. They are not matched against the messages. |
labels | Label tokens (<label>:<value>) in all (every token), any (groups of tokens: one match per group) and none (true/false tokens to exclude). |
quality, service, trust | Bounds on the three scores, each as { "min": 0.5, "max": 1 }, from 0 to 1. |
labelStatus | labeled, pending, failed or not_eligible. |
pageType | The kind of page the conversation started from: home, category, product or checkout. A conversation with no recorded page never matches. |
productId, categoryName | The exact product id or category name of the page the conversation started from, as your tag declared it (1 to 200 characters). |
entryWidgetId | An engagement widget id (ewgt_...): conversations opened from that widget, whichever version of it. |
commerce | Commerce outcomes addedToCart, reachedCheckout, purchased, each true or false. See Commerce outcomes. |
limit | Page size, 25 by default, at most 100. |
cursor | The nextCursor of the previous page. Send it with the same filters. |
count | Also return how many conversations match, capped at 10,000. |
A token the taxonomy doesn't define is refused, and the error names it and points to list_label_vocabulary. The rules for labels.none, score bounds and cursors are the ones on Filter and paginate conversations. A field the tool doesn't define is refused, never ignored.
The five fields from pageType down exist only on the MCP tool. The REST conversation list doesn't take them: it refuses them as unknown query parameters. Filters combine: a conversation has to match all of the ones you set. The page of origin and the entry widget of each result are in its pageContext and entryWidget, which is where to read a productId or an entryWidgetId to filter on. When a page filter finds nothing, the text adds that a conversation whose page of origin wasn't recorded never matches one.
Commerce outcomes
Every item of a search carries commerce with three outcomes, addedToCart, reachedCheckout and purchased, and the result carries commerceTrackingSince: the time of your organization's first add-to-cart, checkout or purchase event, or null if it has none.
Each outcome is true, false or null:
true: an event of that stage was recorded for the conversation. Each outcome reads its own event: a purchase doesn't imply a checkout.false: the conversation was created aftercommerceTrackingSinceand no event of that stage was recorded for it.null: unknown. The conversation was created beforecommerceTrackingSince, or the organization has no commerce event at all. It is neverfalse, because it may well have reached that stage before anything was recorded.
The commerce filter follows the same rule. true keeps the conversations with a recorded event. false keeps only the ones that were measured and had none. A conversation whose outcome is unknown matches neither.
Today these events aren't recorded. Nothing in the product writes add-to-cart, checkout or purchase events yet. Until it does, commerceTrackingSince is null, every outcome is null and a commerce filter matches nothing, false included. The search still answers, and the text says so. Once events are recorded for your organization, the outcomes appear without any change on your side, for conversations from that point on.
No revenue and no order value come back, only the three yes/no stages.
Read conversations
get_conversationreturns the full transcript: each message has arole(shopperorassistant), an orderedcontentlist of typed blocks (text, product cards, questions, quick replies, hand-off, files) and the shopper'sfeedbackrating, ornullif there is none. It also returns the conversation's site, agent, channel, language, page of origin, entry widget, message count, duration, product engagement, label status and label summary. This is the shape Get a conversation returns, plus the site's name and the dashboard link. Your client should ignore block types it doesn't know.get_conversationsis for comparing or reviewing a set. The transcripts are compact: text is kept, and every other block becomes a one-line marker such as[product cards: 3 shown (...)]. Each conversation carries its label record, ornullwhen it isn'tlabeled. An id that is unknown, or that belongs to another organization, comes back innotFound. The call refuses more than 20 ids.get_conversation_labelsreturns the label record as the REST labels endpoint does. Theevidenceof an answer lists message ids. To read those messages, useget_conversation.
If an id doesn't exist, or belongs to a conversation outside your organization, the tool returns an error. It won't tell you which case it was.
Read a report
get_report returns one of the dashboard's reports for one site, from the same report functions the dashboard pages read.
| Field | Does what |
|---|---|
siteId | Required. A site id from list_sites. Reports are per site. |
report | Required. conversations, quality, engagement or starter_questions. |
from | Required. The first day, YYYY-MM-DD, UTC, included. |
to | Required. The last day, YYYY-MM-DD, UTC, included. |
compare | Also read the previous period, of the same length and ending the day before from. Not available for starter_questions. |
A period can span at most 366 days. A to before from, a day that doesn't exist (2026-02-30) or a compare on starter_questions is refused with a message that says what to change.
What each report returns:
conversations: the total, average messages and duration per conversation, the assisted rate (the share of conversations where the assistant used a tool), conversations per day, per agent, per language and per UTC hour, tool usage, and product-card impressions and clicks. The text also gives the volume week by week (Monday to Sunday, UTC, with the first and last week cut to your period and labeled with their first and last day) and, for a period of 14 days or fewer, day by day. Withcompare, the previous period gets the same lines. Thevolumefield of the result holds every day.quality: ratings, positive and negative counts, the satisfaction rate, the share of rated conversations with a negative rating, ratings per day, negative ratings by category and ratings per agent. The rating comments and the conversation titles of the dashboard's "recent negatives" list aren't included.engagement: the widget funnel (sessions the widget was shown to, views, opens, conversations started), the engagement rate, a split by device and opens by page type. Starts are counted only from the date the result gives instartedCountedSince, so earlier days read low.starter_questions: for each question shown in the period, impressions, clicks and click rate, in total and per surface, most shown first, plus how many questions of the library weren't shown. The label is the question in the site's default language, or its first language if that one is missing.
Days and hours are UTC, as in the dashboard, and the result says timeZone: "UTC". The conversations and quality reports count the Web and Hosted Page channels and leave out the Playground, which is the dashboard's default filter. You can't change that from the tool. engagement and starter_questions read widget events and aren't split by channel.
A rate or an average is null, not 0, when there is nothing to divide: a day range without conversations has no assisted rate, rather than a rate of 0%. Treat null as "no data". With compare, every summary figure comes as current and previous; previous is null when you didn't ask.
Break down a label
get_label_breakdown counts how many labeled conversations hold each value of one label. It answers "what are the top transfer reasons this week" in one call, where a search would return conversations one page at a time.
It takes the filters of search_conversations, parsed by the same code, except limit, cursor, count and labelStatus, plus:
| Field | Does what |
|---|---|
label | Required. A label key such as resolution, or a derived key such as transfer_reason or containment: the part of a token before the colon, as list_label_vocabulary lists them. |
compare | Also break down the previous period, of the same length and ending where this one starts, and return the change per value. Needs both from and to. |
A breakdown has to be bounded: send both from and to, or a positive label filter (labels.all or labels.any). labels.none on its own doesn't count. Here from and to are ISO 8601 instants with to excluded, as in a search, not the whole days get_report takes. Unlike get_report, no channel is left out unless you pass channel, so Playground test conversations count.
The result holds, for the period (current) and, with compare, the one before (previous):
values: every value the label can take, with its token,valueandcount, most frequent first. A value nobody has comes back with0, so two periods line up value by value. A yes/no label has one value,true.total: the labeled conversations the counts cover.withoutValue: of those, how many hold no value for this label (a conversation that wasn't transferred has notransfer_reason, for instance).notLabeled: conversations that match the filter but have no labels (pending, failed or not eligible). They are in no count. This number is also capped.cappedandcap: the scan covers the newest 10,000 labeled conversations of the filter. When more match,cappedistrueand the counts cover only those 10,000. Narrow the dates for exact counts.
With compare, delta gives the change in total and in each value's count, now minus then. It isn't exact when either period is capped, and the text says so. When no label or score filter is set, the text also warns when a period is only partly labeled, meaning more of its conversations have no labels than have some (for example 3 labeled and 73 not): the changes then compare unequal samples, so the text calls them apparent and leaves the numbers as they are. Both periods print their instants the same way. A commerce filter works here too, and the result then carries commerceTrackingSince.
A label key the taxonomy doesn't define is refused, and the error points to list_label_vocabulary. The counts agree with search: the count for a token equals the count of search_conversations filtered on that token with the same filters, up to the cap. As everywhere, only labeled conversations are counted: see Labels and the labeled channels.
Read an agent version
get_agent_version shows what one version of an agent runs with, so the assistant can explain how a conversation was handled or what a release changed.
| Field | Does what |
|---|---|
agentVersionId | Required. An agent version id (agtv_...). list_sites gives the id of each agent's live version. The result of get_conversation, get_conversations and search_conversations gives the id of the version that handled each conversation. |
baseVersionId | Optional. Another version of the same agent. The result then also says what changed from it to agentVersionId. |
It returns the version number, its status (draft, candidate, champion or archived, whichever it is) and the engine's display name, then your instructions in order (each block with its name, slug, type, text and condition), the tools the version enables, and the other versions of the same agent, newest first. Tools here are the assistant's built-in ones. Connector tools aren't listed.
With baseVersionId, the diff lists the instruction blocks added, removed and changed (with which of name, type, text and condition changed, and the text before and after), whether the shared blocks run in a different order, the tools added and removed, and the engine change when there is one. Blocks are matched by slug. Comparing a version with itself gives an empty diff. Two versions of different agents are refused.
The engine's internal id, the base persona that comes with the engine, the model and its thinking level are never returned. The instructions are your own configuration text: the server tells the assistant to analyze it, not to follow it.
This tool is gated by a permission. It works for a member who holds Agents: View (agents:view) or Agents: Edit (agents:edit) in your organization, the bar for opening an agent in the dashboard. Every other tool is open to any member of the organization, because the pages that show the same data don't check a permission.
- Resolved on every call. The server looks up the member's current roles with WorkOS each time the tool is called and never caches the answer, so a role change applies on the next call.
- Refused when it can't be resolved. If the lookup fails, or finds no active membership, the call is refused exactly as if the role lacked the permission. The two are indistinguishable.
- A refusal reveals nothing. The permission is checked before the version is looked up, and the error only names the permission and says to ask an administrator. It doesn't say whether the version exists. A version that doesn't exist, belongs to another organization or belongs to a deleted agent all answer the same "No agent version with that id exists in this organization."
To grant the permission, edit the member's role under Roles.
Errors
A failure comes back as a tool error, not as a broken connection, with a message the assistant can act on. A wrong input names the problem. A conversation or site that isn't visible to you answers one message whether it is unknown or someone else's. A server fault answers "The request failed. Try again." and nothing more.
Prompts
The server also offers three prompts. A client such as Claude Desktop lists them as ready-made requests. Each one is a template that tells the assistant which tools to call, in what order, and how to structure its answer in four parts. It must cite the message id behind each claim. They hold none of your data, only the arguments you type.
| Prompt | Arguments | Does what |
|---|---|---|
analyze_conversation | conversationId | Reads one conversation and its labels, then explains what happened and why. |
compare_conversations | conversationIds (comma-separated, 2 to 20) or filter (a plain description) | Compares the conversations you name, or finds the ones a description matches, then says what they share and where they differ. |
weekly_review | siteId (optional), weekOf (optional date, YYYY-MM-DD) | Reviews one Monday-to-Sunday week (UTC) for a site or the whole organization: volume, problems, what worked and three changes to make. |
compare_conversations needs conversationIds or filter. With neither, or with ids that aren't conversation ids, the client gets an error. weekly_review without weekOf reviews the last complete week before the asOf time the tools return.
A worked example
You ask: "Find unresolved conversations about sizing last week."
The assistant starts with no list of valid label tokens and no clock, so it calls the tools in this order. The arguments below are what a client would plausibly send. The dates assume the server's asOf is 2026-10-01, a Thursday, so last week is Monday 2026-09-21 to Sunday 2026-09-27.
list_label_vocabulary
No arguments. The assistant reads the tokens the search accepts, with an English display name for each label, value and block, the same names the REST vocabulary returns. It uses the names to explain a label to you and filters on the tokens. The taxonomy has no label named "sizing". The size-related tokens it can use are objection_size_fit:true (a purchase objection about size or fit), gap_info_topic:size_fit (the product information the shopper needed about size was missing) and capability_needed:size_recommendation (the shopper needed the assistant to turn measurements into a size). Resolution has the value unresolved.
search_conversations
{
"from": "2026-09-21T00:00:00Z",
"to": "2026-09-28T00:00:00Z",
"labels": {
"all": ["resolution:unresolved"],
"any": [
[
"objection_size_fit:true",
"gap_info_topic:size_fit",
"capability_needed:size_recommendation"
]
]
},
"count": true
}Every site is searched, since no siteId is given. all requires resolution:unresolved. The one group in any needs at least one of the three size tokens. The result is a page of compact items, newest first (id, title, site, agent, channel, language, ratings, label summary, dashboard link), a count, and a nextCursor when there are more than the 25 default. The assistant reads the next page by calling again with the same filters and that cursor.
get_conversations
{
"conversationIds": [
"conv_01kvnwmagvf27rc9cze5rm9e1r",
"conv_01kvnwmagwf27rc9d7zef4qksf"
]
}The ids are the first ones from the search (the values here are made up). The assistant gets each transcript in compact form with its label record, and reads where the answer failed. For a single conversation it calls get_conversation instead, to see the full content blocks. To see why the judge labeled one conversation as it did, it calls get_conversation_labels and resolves the message ids it cites with get_conversation.
It then answers from what the tools returned, with the dashboard links of the conversations it cites. Results vary with your data and aren't shown here.
A report and a breakdown with comparison
You ask: "How did last week go on our UK site, and why were conversations handed off to a human?"
The dates assume asOf is 2026-10-01 again, so last week is Monday 2026-09-21 to Sunday 2026-09-27. The assistant calls list_sites first to turn "our UK site" into a site id.
get_report
{
"siteId": "site_01kvnwmagvf27rc9cze5rm9e1r",
"report": "conversations",
"from": "2026-09-21",
"to": "2026-09-27",
"compare": true
}Both days are included, so this reads seven UTC days. compare adds the seven days before, 2026-09-14 to 2026-09-20. The result gives each summary figure as current and previous, with the daily volume of both periods. A figure with nothing to divide is null.
get_label_breakdown
{
"label": "transfer_reason",
"siteId": "site_01kvnwmagvf27rc9cze5rm9e1r",
"from": "2026-09-21T00:00:00Z",
"to": "2026-09-28T00:00:00Z",
"compare": true
}to is excluded, so this covers the same seven days. The result lists every transfer reason with its count for the week, its count the week before and the change, plus how many labeled conversations had no transfer reason (withoutValue) and how many matching conversations have no labels yet (notLabeled). If capped is true, the counts cover only the newest 10,000 labeled conversations and the changes aren't exact. The assistant can then call search_conversations with a token such as transfer_reason:capability_missing to read the conversations behind a count.
The site id here is made up, like the ones above. Results vary with your data and aren't shown here.
Labels and the labeled channels
Labels come from the conversation labeling pipeline, which runs in the background. It labels conversations on the Web, Hosted Page and Playground channels (web, hosted, playground), every channel a conversation can come from.
- Labels usually arrive 30 to 90 minutes after a conversation goes quiet, sometimes later. An hourly pass labels a conversation once it has had no new message for 30 minutes. A recent conversation can read
pending: it has no labels yet, a label filter can miss it, andget_conversation_labelsreturns the status and no answers. - An older conversation isn't labeled. One whose last activity is more than 30 days old isn't picked up.
- A conversation that isn't
labeledhas no labels. Itslabelsfield isnulland itslabelSummaryisnull. The status ispending,failedornot_eligible: see Label status for what each means. Fornot_eligible, the server doesn't say which reason applies. - A missing label means "not labeled", not "no issue". A search on a label token returns only labeled conversations, so a token that is absent says nothing about a conversation that hasn't been labeled.
- Label names are for people. The text a tool returns writes a label as its key with an English name beside it, for example
grounded (Stuck to known facts). The name explains the label. Filters and the structured result use the keys and tokens, never the names. - The assistant is told this at connection. The server sends instructions when a session starts, so your client's model is asked to say that a conversation isn't labeled rather than guess its labels, and to filter on tokens, not on names.
To see which conversations a label filter can't cover yet, search with labelStatus set to pending or failed.
Rate limit
Tool calls are limited per organization: 300 calls per minute, shared by every member and every client connected to your organization, across all nine tools. The limit is there so that one client stuck in a loop can't take the server's reads away from the others.
Over the limit, a tool answers an error: "Your organization is making too many requests. Wait 60 seconds, then retry." The call reads nothing. Rely on that retry hint rather than on the figure: 300 is a starting value, and iAdvize can change it.
What the server doesn't give you
- Anything outside your organization. Every read is scoped to the organization you authorized.
- Shopper identity. No field of a conversation identifies the shopper. What a shopper typed is still in the transcript. A shopper identifier such as
visitorIdin a tool's input is refused as an unknown field. - Reasoning, cost and token usage. None of it is in a tool's result.
- Tool inputs and outputs of the shopper chat. A transcript shows what the shopper saw (text, product cards, questions, quick replies, hand-off, files), not the assistant's internal tool calls.
- Site traffic. The server has no visits, page views, bounce rate or conversion rate of your site. The engagement report counts the sessions the assistant's widget was shown to, not the visitors of your site.
- Revenue and order value. The commerce outcomes of a conversation are yes, no or unknown, with no amount. Today they are all unknown: see Commerce outcomes.
- Market and competitor data. The server only knows the conversations your assistant had. It has nothing on your market, your competitors' prices or what shoppers do elsewhere.
- Catalog data beyond what a product card showed the shopper (current prices, stock), what a human agent said after a hand-off, and benchmarks across organizations. This data isn't in the server.
The assistant is told not to estimate any of this. A question that needs it gets an answer that says so, or a figure from another tool that you should not read as a substitute.
This gives your MCP client the same visibility as a dashboard user
A conversation transcript can contain shopper-provided personal information. Connecting an MCP client gives it exactly the same access a person logged into your dashboard already has: nothing more, nothing less. Only connect clients and organizations you trust with that data.
Limitations today
- Read-only. Nothing an MCP client does through this server can change your agents, versions, or live shopper experience.
- Reports are per site and cover four reports.
get_reportreads conversations, quality, engagement and starter questions for one site at a time. There is no cross-site total, and no filter by agent, language or keyword on a report. - Commerce outcomes are unknown for now. Until add-to-cart, checkout and purchase events are recorded for your organization, they come back
null. See Commerce outcomes. - Labels take time and cover recent conversations. See Labels and the labeled channels.