MCP tools
The server exposes 12 tools. All are read-only and pinned to the connected property, so none takes a property argument.
| Tool | Answers | Required |
|---|---|---|
count_sessions | How many sessions in a window | window |
metrics | Sessions, views, duration, scroll, bounce rate, cost, value over a window | window, metrics |
breakdown | Top values of a field (country, device, UTM, channel…) with counts | window, field |
time_series | Metrics bucketed by hour, day, week or month | window |
compare_windows | This window vs the equal-length window before it | window |
list_conversions | The conversions defined on the property | none |
conversion_summary | Entries, conversions, rate and value for one conversion | conversion_id, window |
conversion_funnel | Step-by-step counts and drop-off for one conversion | conversion_id, window |
report_history | Rows from stored weekly reports, newest first | none |
ad_performance | Spend, clicks, attributed sessions, CPA and ROAS per ad platform | window |
ad_spend | Daily spend rows and top campaigns | window |
ads_doctor | Diagnosis of an ad platform connection | none |
Every tool returns a text block containing JSON in one of two shapes:
{ "ok": true, "data": { ... } }{ "ok": false, "error": { "code": "tool_error", "message": "..." } }Error codes you will see: tool_error (default, message explains), invalid_field (breakdown field not in the list), invalid_metrics (no valid metric given to metrics).
Windows
Section titled “Windows”Every time-bound tool takes a window. The accepted values:
| Window | Covers |
|---|---|
live | The last 5 minutes |
today | Midnight UTC to now |
yesterday | The previous calendar day |
last_24h | Same as today |
prev_24h | Same as yesterday |
last_7 | The last 7 days |
last_28 | The last 28 days |
month_to_date | The 1st of this month to now |
year_to_date | 1 January to now |
last_12_months | The last 12 months |
last_week | The last completed ISO calendar week, Monday to Sunday (UTC) |
prev_week | The week before last_week |
2026-07-01..2026-07-31 | A literal inclusive date range, YYYY-MM-DD..YYYY-MM-DD |
All windows are resolved in UTC. Anything else fails with Unsupported window: ... Supported: live, today, .... The server’s instructions tell the assistant to prefer a named window over a date range.
Every data payload echoes the resolved window as { "from_ts": 1755129600, "to_ts": 1755734399, "label": "last_7" }, unix seconds.
Metrics
Section titled “Metrics”Used by metrics, time_series and compare_windows.
| Metric | Meaning | Unit |
|---|---|---|
sessions | Number of sessions | count |
views | Total page views (sum of views across sessions) | count |
views_per_session | Average page views per session | number |
avg_duration | Average time on site | seconds |
avg_scroll | Average of each session’s deepest scroll | percent, 0 to 100 |
bounce_rate | Share of sessions that bounced | ratio, 0 to 1 |
cost_usd | Total acquisition cost stamped on the sessions | USD cents |
value_usd | Total value stamped on the sessions | USD cents |
cost_usd and value_usd are the USD-normalised money families, so they sum cleanly across currencies; CPA and ROAS are the two divided. A metric with no data comes back null, not 0.
The REST graph endpoint names these differently (views_session, bounce, duration in minutes, scroll). When a question spans both surfaces, compare definitions, not names.
Audience
Section titled “Audience”count_sessions, metrics, breakdown, time_series and compare_windows accept audience:
| Value | Sessions included |
|---|---|
user (default) | Sessions not classified as a bot |
bot | Bot sessions only |
any | Both |
Shape parameters
Section titled “Shape parameters”The same five signals tools accept four optional parameters that narrow the sessions being counted. They combine with AND.
filters
Section titled “filters”Equality filters. An object whose keys are filter fields and whose values are a string (exact match) or an array of strings (any of, up to 20 values).
{ "filters": { "device": "mobile", "country_code": ["NO", "SE", "DK"] } }- At most 5 keys, else the call fails with
Too many filter keys. Max 5. - Unknown keys are dropped silently; the remaining filters still apply.
- String values are truncated to 256 characters.
numeric_filters
Section titled “numeric_filters”Range filters on the five numeric fields. Operators: gte, lte, gt, lt.
{ "numeric_filters": { "time_on_site": { "gte": 60 }, "views": { "gte": 3, "lt": 10 } } }| Field | Unit |
|---|---|
time_on_site | seconds |
views | page views in the session |
avg_max_scroll | percent, 0 to 100 |
screen_width | pixels |
screen_height | pixels |
Unknown fields, unknown operators and non-numeric values are dropped silently.
bounced
Section titled “bounced”true counts only bounced sessions, false only non-bounced. Omit for both.
Sessions that fired a named event, optionally with a value condition.
{ "event": { "name": "purchase" } }{ "event": { "name": "scroll", "value": "50", "value_match": "gte" } }value_match is equals (default, string comparison) or one of gte, lte, gt, lt (numeric comparison against the event’s numeric value). An event object without a name is ignored.
Filter fields
Section titled “Filter fields”The keys accepted by filters and by breakdown’s field. Where the name differs from the REST field catalog, the REST id is shown.
| Field | Meaning | REST id |
|---|---|---|
country | Country name | same |
country_code | ISO 3166-1 alpha-2 country code | same |
continent | Continent code | same |
region | Region / state name | same |
city | City name | same |
lang | Browser language | language |
timezone | Visitor timezone | same |
device | Device class (desktop, mobile, tablet…) | same |
device_brand | Device brand | same |
device_model | Device model | same |
browser | Browser name | same |
browser_family | Browser family | same |
os | Operating system | same |
os_version | Operating system version name | same |
entry_path | Path of the first page in the session | same |
entry_subdomain | Subdomain of the first page | same |
entry_title | Title of the first page | same |
exit_path | Path of the last page | same |
exit_subdomain | Subdomain of the last page | same |
referrer_domain | Referring domain | same |
utm_source | utm_source on entry | same |
utm_medium | utm_medium on entry | same |
utm_campaign | utm_campaign on entry | same |
channel_category | Channel: direct, organic_search, organic_social, paid_search, paid_social, email, referral | channel |
channel_name | Channel name as classified | same |
source_name | Traffic source name | source |
screen_width | Viewport width in pixels | same |
screen_height | Viewport height in pixels | same |
screen_width and screen_height are integers but are deliberately groupable: automation clusters on a handful of viewport defaults (800x600, 0x0) that real visitors barely touch, and “what are people actually viewing this on” is an ordinary question.
Limits
Section titled “Limits”| Limit | Value |
|---|---|
| Filter keys per call | 5 |
| Values per array filter | 20 |
| Filter value length | 256 characters |
breakdown rows | 20 (default 10) |
time_series buckets | 90; a longer window at a fine interval fails with Time series exceeds 90 buckets |
report_history rows | 12 (default 8) |
ad_spend top campaigns | 10 |
| Requests per minute | 60 per token |
Tool reference
Section titled “Tool reference”count_sessions
Section titled “count_sessions”Count sessions in a window.
| Parameter | Type | Required | Notes |
|---|---|---|---|
window | string | yes | See Windows |
audience | user | bot | any | no | Default user |
filters, numeric_filters, bounced, event | no | See Shape parameters |
{ "property_id": "...", "window": { "from_ts": 1755129600, "to_ts": 1755734399, "label": "last_7" }, "audience": "user", "count": 4180 }metrics
Section titled “metrics”One or more metrics aggregated over a window.
| Parameter | Type | Required | Notes |
|---|---|---|---|
window | string | yes | |
metrics | string[] | yes | From Metrics. Unknown names are dropped; if none remain the call fails with invalid_metrics |
audience | string | no | Default user |
| shape params | no |
{ "property_id": "...", "window": { ... }, "audience": "user", "metrics": { "sessions": 4180, "avg_duration": 94.2, "bounce_rate": 0.41 } }breakdown
Section titled “breakdown”Top values of one field with counts, highest first.
| Parameter | Type | Required | Notes |
|---|---|---|---|
window | string | yes | |
field | string | yes | One of the filter fields; anything else fails with invalid_field |
top | integer | no | Default 10, clamped to 1..20 |
audience | string | no | Default user |
| shape params | no |
{ "property_id": "...", "window": { ... }, "field": "channel_category", "rows": [ { "value": "organic_search", "count": 1930 }, { "value": "direct", "count": 1204 } ] }time_series
Section titled “time_series”Metrics per time bucket.
| Parameter | Type | Required | Notes |
|---|---|---|---|
window | string | yes | |
interval | hour | day | week | month | no | Omit to let the server pick from the window length |
metrics | string[] | no | Default ["sessions"] |
audience | string | no | Default user |
| shape params | no |
{ "property_id": "...", "window": { ... }, "interval": "day", "metrics": ["sessions"], "buckets": [ { "key": "2026-08-15", "from_ts": 1755216000, "to_ts": 1755302399, "metrics": { "sessions": 602 } }, { "key": "2026-08-16", "from_ts": 1755302400, "to_ts": 1755388799, "metrics": { "sessions": 577 } } ]}compare_windows
Section titled “compare_windows”The window against the equal-length span immediately before it. For last_7 that is the 7 days before those; for month_to_date it is the same number of days ending where this window starts.
| Parameter | Type | Required | Notes |
|---|---|---|---|
window | string | yes | The current window |
metrics | string[] | no | Default ["sessions"] |
audience | string | no | Default user |
| shape params | no | Applied to both windows |
{ "property_id": "...", "current": { "window": { "label": "last_7", ... }, "metrics": { "sessions": 4180 } }, "previous": { "window": { "label": "last_7_previous", ... }, "metrics": { "sessions": 3902 } }, "delta": { "sessions": { "absolute": 278, "percent": 7.12 } }}percent is null when the previous value is 0 or missing.
list_conversions
Section titled “list_conversions”No parameters. Call it first; the other conversion tools need a conversion_id from here.
{ "property_id": "...", "conversions": [ { "id": "...", "name": "Newsletter signup", "is_active": true, "type_label": "Lead", "state_label": "Active", "steps_count": 2 } ]}conversion_summary
Section titled “conversion_summary”| Parameter | Type | Required |
|---|---|---|
conversion_id | string | yes |
window | string | yes |
{ "conversion_id": "...", "conversion_name": "Newsletter signup", "property_id": "...", "window": { ... }, "summary": { "conversions": 212, "sessions": 4180, "rate": 5.1, "value": 0, "value_currency": "USD", "value_formatted": "$0.00", "value_usd": 0 }}sessions is the number of sessions that entered the funnel (matched the first step), rate is a percentage with one decimal, value is in minor units of value_currency.
A conversion_id from another property, even one on the same account, fails with This token is scoped to a single property.
conversion_funnel
Section titled “conversion_funnel”| Parameter | Type | Required |
|---|---|---|
conversion_id | string | yes |
window | string | yes |
{ "conversion_id": "...", "conversion_name": "Checkout", "property_id": "...", "window": { ... }, "steps": [ { "step_id": "...", "name": "Viewed pricing", "order": 0, "trigger_label": "Page view", "trigger_summary": "/pricing", "count": 1500, "drop_off": 0, "drop_off_count": 0, "conversion_rate": 100, "is_conversion": false }, { "step_id": "...", "name": "Purchased", "order": 1, "trigger_label": "Event", "trigger_summary": "purchase", "count": 90, "drop_off": 94, "drop_off_count": 1410, "conversion_rate": 6, "is_conversion": true } ]}drop_off is the percentage lost since the previous step; conversion_rate is the step’s count as a percentage of entries. A conversion with a single step returns an empty steps array.
report_history
Section titled “report_history”Rows from stored weekly reports. Reads the frozen snapshots; it does not re-query analytics.
| Parameter | Type | Required | Notes |
|---|---|---|---|
limit | integer | no | Default 8, clamped to 1..12 |
{ "property_id": "...", "currency_note": "Spend and value figures are in cents.", "reports": [ { "period_key": "2026-W33", "period_label": "10 - 16 Aug 2026", "sessions": 4180, "sessions_wow_percent": 7.1, "conversions": [ { "name": "Newsletter signup", "conversions": 212, "rate": 5.1, "value": 0, "value_currency": "USD" } ], "ads": [ { "platform": "google", "spend": 125000, "currency_code": "USD", "attributed_sessions": 640, "cpa": 1389, "roas": 2.4 } ], "check_keys": ["traffic_drop"] } ]}Only reports in the ready state are returned, newest first.
ad_performance
Section titled “ad_performance”Per-platform performance for the window. Needs a connected ad platform.
| Parameter | Type | Required |
|---|---|---|
window | string | yes |
Returns performance: { has_ads, platforms: [...], totals: {...} }. Each platform row carries spend (cents, in the account’s billing currency), impressions, clicks, CPC, attributed sessions and, when a conversion is linked to the integration, conversions, revenue, CPA and ROAS. currency_mismatch is set when spend and revenue are in different currencies, in which case ROAS is not a like-for-like number. has_ads is false with an empty list when nothing is connected.
ad_spend
Section titled “ad_spend”Daily spend rows plus the top campaigns, for “when did spend spike”.
| Parameter | Type | Required | Notes |
|---|---|---|---|
window | string | yes | |
platform | google | meta | linkedin | tiktok | no | Limit to one platform |
{ "property_id": "...", "window": { ... }, "platform": null, "currency_note": "All spend values are in cents.", "days": [ { "date": "2026-08-15", "spend": 18200, "clicks": 310, "impressions": 12800 } ], "top_campaigns": [ { "platform": "google", "campaign_name": "Brand - NO", "spend": 61000 } ]}At most 10 campaigns. Google Ads and Meta are the platforms that can be connected today; linkedin and tiktok are accepted by the schema but have no integration yet.
ads_doctor
Section titled “ads_doctor”Diagnoses a platform connection: token and connection state, the chosen ad account, the cached account picker list, sync status and, for Google, the raw per-account API responses the picker is built from. Use it when accounts are missing from the picker, the picker is empty, or spend sync errors.
| Parameter | Type | Required | Notes |
|---|---|---|---|
platform | google | meta | linkedin | tiktok | no | Limit to one platform |
Returns integrations: [...] with one report per connected platform, or note: "No ad platform integrations exist on this property." when there are none. The Google probe makes live API calls and can take up to a minute on logins with many accounts. It refreshes its token in memory and persists nothing.
Example prompts
Section titled “Example prompts”What an assistant does with a question, once connected:
| You ask | Tools called |
|---|---|
| ”How much traffic did we get last week versus the week before?” | compare_windows with window: "last_week" |
| ”What’s our bounce rate on mobile this month?” | metrics with window: "month_to_date", metrics: ["bounce_rate"], filters: { device: "mobile" } |
| ”Where is paid traffic landing?” | breakdown on entry_path with filters: { channel_category: ["paid_search", "paid_social"] } |
| ”Show sessions by day for the last 28 days.” | time_series with window: "last_28", interval: "day" |
| ”Where do people drop out of checkout?” | list_conversions, then conversion_funnel with the matching conversion_id |
| ”Did anyone who watched the demo video end up converting?” | count_sessions with event: { name: "video_play" }, then conversion_summary |
| ”Is the Google Ads account synced? Nothing shows in the picker.” | ads_doctor with platform: "google" |
| ”How did this week compare with the trend?” | report_history, then metrics or compare_windows for the current week |
Guarantees
Section titled “Guarantees”- Read-only. Every tool is bridged through a class annotated read-only and idempotent, and a test scans every class reachable from the server for write calls and repository imports. A tool that could write cannot be registered.
- One property.
property_idis removed from every advertised schema and injected from the token on each call. The tenancy context refuses any other property id, including siblings on the same account, so aconversion_idbelonging elsewhere fails rather than leaks. - Audited. Each call is recorded with its tool name, arguments, result size, duration and outcome against the authorising user.