Skip to content

MCP tools

The server exposes 12 tools. All are read-only and pinned to the connected property, so none takes a property argument.

ToolAnswersRequired
count_sessionsHow many sessions in a windowwindow
metricsSessions, views, duration, scroll, bounce rate, cost, value over a windowwindow, metrics
breakdownTop values of a field (country, device, UTM, channel…) with countswindow, field
time_seriesMetrics bucketed by hour, day, week or monthwindow
compare_windowsThis window vs the equal-length window before itwindow
list_conversionsThe conversions defined on the propertynone
conversion_summaryEntries, conversions, rate and value for one conversionconversion_id, window
conversion_funnelStep-by-step counts and drop-off for one conversionconversion_id, window
report_historyRows from stored weekly reports, newest firstnone
ad_performanceSpend, clicks, attributed sessions, CPA and ROAS per ad platformwindow
ad_spendDaily spend rows and top campaignswindow
ads_doctorDiagnosis of an ad platform connectionnone

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).

Every time-bound tool takes a window. The accepted values:

WindowCovers
liveThe last 5 minutes
todayMidnight UTC to now
yesterdayThe previous calendar day
last_24hSame as today
prev_24hSame as yesterday
last_7The last 7 days
last_28The last 28 days
month_to_dateThe 1st of this month to now
year_to_date1 January to now
last_12_monthsThe last 12 months
last_weekThe last completed ISO calendar week, Monday to Sunday (UTC)
prev_weekThe week before last_week
2026-07-01..2026-07-31A 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.

Used by metrics, time_series and compare_windows.

MetricMeaningUnit
sessionsNumber of sessionscount
viewsTotal page views (sum of views across sessions)count
views_per_sessionAverage page views per sessionnumber
avg_durationAverage time on siteseconds
avg_scrollAverage of each session’s deepest scrollpercent, 0 to 100
bounce_rateShare of sessions that bouncedratio, 0 to 1
cost_usdTotal acquisition cost stamped on the sessionsUSD cents
value_usdTotal value stamped on the sessionsUSD 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.

count_sessions, metrics, breakdown, time_series and compare_windows accept audience:

ValueSessions included
user (default)Sessions not classified as a bot
botBot sessions only
anyBoth

The same five signals tools accept four optional parameters that narrow the sessions being counted. They combine with AND.

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.

Range filters on the five numeric fields. Operators: gte, lte, gt, lt.

{ "numeric_filters": { "time_on_site": { "gte": 60 }, "views": { "gte": 3, "lt": 10 } } }
FieldUnit
time_on_siteseconds
viewspage views in the session
avg_max_scrollpercent, 0 to 100
screen_widthpixels
screen_heightpixels

Unknown fields, unknown operators and non-numeric values are dropped silently.

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.

The keys accepted by filters and by breakdown’s field. Where the name differs from the REST field catalog, the REST id is shown.

FieldMeaningREST id
countryCountry namesame
country_codeISO 3166-1 alpha-2 country codesame
continentContinent codesame
regionRegion / state namesame
cityCity namesame
langBrowser languagelanguage
timezoneVisitor timezonesame
deviceDevice class (desktop, mobile, tablet…)same
device_brandDevice brandsame
device_modelDevice modelsame
browserBrowser namesame
browser_familyBrowser familysame
osOperating systemsame
os_versionOperating system version namesame
entry_pathPath of the first page in the sessionsame
entry_subdomainSubdomain of the first pagesame
entry_titleTitle of the first pagesame
exit_pathPath of the last pagesame
exit_subdomainSubdomain of the last pagesame
referrer_domainReferring domainsame
utm_sourceutm_source on entrysame
utm_mediumutm_medium on entrysame
utm_campaignutm_campaign on entrysame
channel_categoryChannel: direct, organic_search, organic_social, paid_search, paid_social, email, referralchannel
channel_nameChannel name as classifiedsame
source_nameTraffic source namesource
screen_widthViewport width in pixelssame
screen_heightViewport height in pixelssame

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.

LimitValue
Filter keys per call5
Values per array filter20
Filter value length256 characters
breakdown rows20 (default 10)
time_series buckets90; a longer window at a fine interval fails with Time series exceeds 90 buckets
report_history rows12 (default 8)
ad_spend top campaigns10
Requests per minute60 per token

Count sessions in a window.

ParameterTypeRequiredNotes
windowstringyesSee Windows
audienceuser | bot | anynoDefault user
filters, numeric_filters, bounced, eventnoSee Shape parameters
{ "property_id": "...", "window": { "from_ts": 1755129600, "to_ts": 1755734399, "label": "last_7" }, "audience": "user", "count": 4180 }

One or more metrics aggregated over a window.

ParameterTypeRequiredNotes
windowstringyes
metricsstring[]yesFrom Metrics. Unknown names are dropped; if none remain the call fails with invalid_metrics
audiencestringnoDefault user
shape paramsno
{ "property_id": "...", "window": { ... }, "audience": "user", "metrics": { "sessions": 4180, "avg_duration": 94.2, "bounce_rate": 0.41 } }

Top values of one field with counts, highest first.

ParameterTypeRequiredNotes
windowstringyes
fieldstringyesOne of the filter fields; anything else fails with invalid_field
topintegernoDefault 10, clamped to 1..20
audiencestringnoDefault user
shape paramsno
{ "property_id": "...", "window": { ... }, "field": "channel_category", "rows": [ { "value": "organic_search", "count": 1930 }, { "value": "direct", "count": 1204 } ] }

Metrics per time bucket.

ParameterTypeRequiredNotes
windowstringyes
intervalhour | day | week | monthnoOmit to let the server pick from the window length
metricsstring[]noDefault ["sessions"]
audiencestringnoDefault user
shape paramsno
{
"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 } }
]
}

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.

ParameterTypeRequiredNotes
windowstringyesThe current window
metricsstring[]noDefault ["sessions"]
audiencestringnoDefault user
shape paramsnoApplied 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.

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 }
]
}
ParameterTypeRequired
conversion_idstringyes
windowstringyes
{
"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.

ParameterTypeRequired
conversion_idstringyes
windowstringyes
{
"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.

Rows from stored weekly reports. Reads the frozen snapshots; it does not re-query analytics.

ParameterTypeRequiredNotes
limitintegernoDefault 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.

Per-platform performance for the window. Needs a connected ad platform.

ParameterTypeRequired
windowstringyes

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.

Daily spend rows plus the top campaigns, for “when did spend spike”.

ParameterTypeRequiredNotes
windowstringyes
platformgoogle | meta | linkedin | tiktoknoLimit 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.

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.

ParameterTypeRequiredNotes
platformgoogle | meta | linkedin | tiktoknoLimit 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.

What an assistant does with a question, once connected:

You askTools 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
  • 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_id is 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 a conversion_id belonging elsewhere fails rather than leaks.
  • Audited. Each call is recorded with its tool name, arguments, result size, duration and outcome against the authorising user.