umami-mcp-server
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| UMAMI_REGION | No | Umami Cloud region: 'us' or 'eu'. Defaults to the key owner's region. | |
| UMAMI_API_KEY | No | Umami API key. Required for Umami Cloud, or as one of the two authentication methods for self-hosted instances. | |
| UMAMI_BASE_URL | No | Root URL of a self-hosted Umami instance, e.g. https://analytics.example.com. The /api suffix is added automatically. | |
| UMAMI_PASSWORD | No | Login password for self-hosted Umami, used together with UMAMI_USERNAME instead of an API key. | |
| UMAMI_TIMEZONE | No | IANA timezone for day boundaries and time-series buckets, e.g. America/New_York. | UTC |
| UMAMI_USERNAME | No | Login username for self-hosted Umami, used together with UMAMI_PASSWORD instead of an API key. | |
| UMAMI_DEFAULT_WEBSITE | No | Website ID, name, or domain used when a tool call omits 'website'. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| umami_list_websitesA | List every website tracked in this Umami account, including websites owned by teams. Start here when you do not already know a website ID. Every other tool accepts a website ID, name, or domain, so this tool is what turns "the marketing site" into something queryable. Args:
Returns: JSON shape: { "count": number, "websites": [ { "id": string, "name": string, "domain": string, "created_at": string, "team_id": string | null } ] } Examples:
Error handling:
|
| umami_get_websiteA | Get configuration details for one website plus the date range of data actually collected for it. The date range matters: querying a period before tracking started returns zeros, which is easy to misread as a traffic collapse. Check this first when numbers look surprisingly empty. Args:
Returns: JSON shape: { "id": string, "name": string, "domain": string, "share_id": string | null, "created_at": string, "data_start": string | null, "data_end": string | null, "active_visitors": number } Examples:
|
| umami_get_active_visitorsA | Get the number of unique visitors active on a website in the last 5 minutes. This is the realtime counter only. For traffic over a period use umami_get_stats. Args:
Returns: JSON shape: { "website_id": string, "active_visitors": number, "window": "last 5 minutes" } Examples:
|
| umami_get_statsA | Get summary traffic statistics for a website over a date range, with optional comparison to the immediately preceding period. This is the headline-numbers tool: pageviews, visitors, visits, bounce rate, and average visit duration. Bounce rate and average visit duration are derived here, since Umami returns raw bounce and total-time counts. Args:
Returns: JSON shape: { "website_id": string, "range": { "start": string, "end": string }, "stats": { "pageviews": number, "visitors": number, "visits": number, "bounces": number, "totaltime": number, "bounce_rate_pct": number, "views_per_visit": number, "avg_visit_duration_seconds": number }, "previous": { ...same fields... } | null, "change": { "pageviews": string, "visitors": string, "visits": string, "bounce_rate_pct": string } | null } Examples:
Error handling:
|
| umami_get_pageviews_seriesA | Get pageviews and sessions bucketed over time, for trend and seasonality questions. Use this when the question is about shape over time rather than a single total: which day spiked, whether traffic is trending up, what the weekday pattern looks like. Args:
Returns: JSON shape: { "website_id": string, "unit": string, "points": [ { "timestamp": string, "pageviews": number, "sessions": number } ], "totals": { "pageviews": number, "sessions": number }, "peak": { "timestamp": string, "pageviews": number } } Examples:
|
| umami_get_events_seriesA | Get counts of custom tracked events bucketed over time, grouped by event name. Use this for conversion and interaction tracking: form submits, button clicks, signups, or any event fired through umami.track(). Args:
Returns: JSON shape: { "website_id": string, "unit": string, "totals_by_event": { "": number }, "series": [ { "event": string, "timestamp": string, "count": number } ] } Examples:
|
| umami_get_metricsA | Get a ranked breakdown of traffic by one dimension: top pages, referrers, countries, browsers, devices, acquisition channels, custom events, and more. This is the workhorse for "top N" questions. Set expanded=true when you need engagement quality per row (pageviews, visitors, visits, bounces, time on site) rather than just a visitor count, for example to find which landing page bounces hardest. Args:
Returns: Plain JSON shape: { "type": string, "count": number, "rows": [ { "name": string, "visitors": number, "share_pct": number } ], "has_more": boolean, "next_offset": number } Expanded JSON shape: { "type": string, "count": number, "rows": [ { "name": string, "pageviews": number, "visitors": number, "visits": number, "bounces": number, "bounce_rate_pct": number, "avg_visit_duration_seconds": number } ], ... } Examples:
Error handling:
|
| umami_list_sessionsA | List individual visitor sessions for a website over a date range, newest first. Sessions are anonymous. Use this to inspect real visit behaviour rather than aggregates: how many pages a typical visit covers, where high-engagement visitors come from, or what a spike actually consisted of. Args:
Returns: JSON shape: { "total": number, "page": number, "page_size": number, "sessions": [ { "id": string, "country": string, "city": string, "device": string, "browser": string, "os": string, "first_at": string, "last_at": string, "visits": number, "views": number } ], "has_more": boolean } Examples:
Error handling:
|
| umami_get_sessionA | Get details for one visitor session, optionally including the full page-by-page activity trail. Use this to trace an individual journey through the site: entry page, path taken, events fired, exit point. Get session IDs from umami_list_sessions with response_format='json'. Args:
Returns: JSON shape: { "session": { "id": string, "country": string, "device": string, "browser": string, "os": string, "first_at": string, "last_at": string, "visits": number, "views": number, "events": number, "totaltime": number }, "activity": [ { "created_at": string, "url_path": string, "referrer_domain": string, "event_name": string } ] } Examples:
|
| umami_traffic_reportA | Build a complete traffic report for a website in one call: headline stats, period-over-period change, and ranked breakdowns for top pages, landing pages, referrers, acquisition channels, countries, devices, and browsers. Prefer this over chaining umami_get_stats and several umami_get_metrics calls when the question is broad, for example "how is the site doing" or "give me last month's analytics". Use the individual tools instead when you need one specific dimension, deeper pagination, or expanded engagement metrics. Args:
Returns: JSON shape: { "website": string, "range": { "start": string, "end": string }, "stats": { "pageviews": number, "visitors": number, "visits": number, "bounce_rate_pct": number, "avg_visit_duration_seconds": number }, "change": { "pageviews": string, "visitors": string, "visits": string } | null, "breakdowns": { "": [ { "name": string, "visitors": number, "share_pct": number } ] } } Examples:
Error handling:
|
| umami_create_websiteA | Register a new website for tracking in Umami. This is the first step in onboarding a client or project: it returns a website ID that goes into the tracking script, and that every stats tool in this server uses. Args:
Returns: JSON shape: { "id": string, "name": string, "domain": string, "team_id": string | null, "created_at": string, "tracking_snippet": string } Examples:
Error handling:
|
| umami_update_websiteA | Update a website's name, domain, public share link, or full session replay and heatmap configuration. Covers every field Umami exposes for a website's recording setup, not just the on/off switches: sampling rates, PII masking strictness, max recording length, and a CSS selector to exclude sensitive elements (payment forms, etc.) from capture. Pass only the fields you want to change; anything omitted is left as-is. Use umami_get_recorder_config afterward to confirm exactly what the tracker will receive. Args:
Returns: JSON shape: { "id": string, "name": string, "domain": string, "share_id": string | null, "replay_config": { "replayEnabled": boolean, "heatmapEnabled": boolean, "sampleRate": number, "heatmapSampleRate": number, "maskLevel": string, "maxDuration": number, "blockSelector": string } | null } Examples:
|
| umami_get_recorder_configA | Get the recorder configuration Umami is actually serving to the tracker for a website: whether replay and heatmaps are enabled, sample rates, masking level, max duration, and the block selector. This reads the same public endpoint the tracker script itself calls, so it is the ground truth after umami_update_website changes replay or heatmap settings, useful for confirming values actually took effect and resolving any unit ambiguity on max duration. Args:
Returns: JSON shape: { "enabled": boolean, "replay_enabled": boolean, "heatmap_enabled": boolean, "sample_rate": number, "heatmap_sample_rate": number, "mask_level": string, "max_duration": number, "block_selector": string } Examples:
|
| umami_reset_websiteA | Permanently delete all collected data for a website: every pageview, session, and event. The website registration and tracking ID are kept, so the tracking script keeps working and data collection starts fresh. This cannot be undone. Requires confirm=true. Args:
Returns: { "ok": true, "website_id": string } Examples:
|
| umami_delete_websiteA | Permanently delete a website registration and all of its collected data from Umami. This cannot be undone. Requires confirm=true. To keep the registration and tracking ID but clear historical data, use umami_reset_website instead. Args:
Returns: { "ok": true, "website_id": string } Examples:
|
| umami_create_userA | Create a new login account on this self-hosted Umami instance. This is for internal team members who need their own login, not for issuing client-facing accounts. Admin access required. Not available on Umami Cloud. Args:
Returns: { "id": string, "username": string, "role": string } Examples:
Error handling:
|
| umami_list_usersA | List every login account on this self-hosted Umami instance. Admin access required. Not available on Umami Cloud. Args:
Returns: JSON shape: { "count": number, "users": [ { "id": string, "username": string, "role": string, "created_at": string } ] } Examples:
|
| umami_get_userA | Get a login account's details, plus the websites and teams it has access to. Admin access required for other users; any authenticated user can look up themselves. Not available on Umami Cloud. Args:
Returns: JSON shape: { "id": string, "username": string, "role": string, "created_at": string, "websites": [ { "id": string, "name": string, "domain": string } ], "teams": [ { "id": string, "name": string } ] } |
| umami_update_userA | Change a login account's username, password, or instance-wide role. Admin access required. Not available on Umami Cloud. Args:
Returns: { "id": string, "username": string, "role": string } Examples:
|
| umami_delete_userA | Permanently delete a login account from this self-hosted Umami instance. The websites they own are not deleted, but become inaccessible to them. This cannot be undone. Requires confirm=true. Admin access required. Not available on Umami Cloud. Args:
Returns: { "ok": true, "user_id": string } |
| umami_create_teamA | Create a team in Umami. Teams group websites and members under shared access, separate from personal accounts. Useful for keeping one client's or one product line's websites together with a dedicated access code. Args:
Returns: { "id": string, "name": string, "access_code": string } The access_code can be shared with someone else so they can self-join via umami_join_team, instead of you adding them one by one. Examples:
|
| umami_list_teamsA | List every team on this Umami account, with member and website counts. Args:
Returns: JSON shape: { "count": number, "teams": [ { "id": string, "name": string, "access_code": string, "website_count": number, "member_count": number } ] } Examples:
|
| umami_get_teamA | Get a team's details, including its full member list and roles. Args:
Returns: JSON shape: { "id": string, "name": string, "access_code": string, "members": [ { "user_id": string, "username": string, "role": string } ] } |
| umami_get_team_websitesA | List every website belonging to a team. Args:
Returns: JSON shape: { "count": number, "websites": [ { "id": string, "name": string, "domain": string } ] } |
| umami_update_teamA | Update a team's name, or set a new access code. Args:
Returns: { "id": string, "name": string, "access_code": string } |
| umami_join_teamA | Join a team as the currently authenticated user, using its access code. This is the self-serve counterpart to umami_add_team_user, which an existing team manager uses to add someone else directly. Args:
Returns: { "team_id": string, "user_id": string, "role": string } |
| umami_add_team_userA | Add an existing Umami login to a team directly, without needing the access code. Requires team-manager or owner permission on the team. Args:
Returns: { "team_id": string, "user_id": string, "role": string } Examples:
|
| umami_update_team_userA | Change an existing team member's role. Args:
Returns: { "team_id": string, "user_id": string, "role": string } |
| umami_remove_team_userA | Remove a member from a team. Their login and any websites they personally own are unaffected; they simply lose access to the team's shared websites. Requires confirm=true. Args:
Returns: { "ok": true, "team_id": string, "user_id": string } |
| umami_delete_teamA | Permanently delete a team. Websites owned by the team are not deleted, but become inaccessible through it; reassign them first if they still need a home. This cannot be undone. Requires confirm=true. Args:
Returns: { "ok": true, "team_id": string } |
| umami_onboard_clientA | Set up everything Umami needs for a new client or project in a single call: register the website, optionally create a dedicated team for it, and optionally grant an existing internal user access to that team. This is the fast path for "get this new site tracked and set up properly." For anything more custom, for example multiple websites under one team, use umami_create_website, umami_create_team, and umami_add_team_user individually. Args:
Returns: JSON shape: { "website": { "id": string, "name": string, "domain": string }, "team": { "id": string, "name": string, "access_code": string } | null, "granted_user": { "id": string, "username": string, "role": string } | null, "replay_config": { "replayEnabled": boolean, "heatmapEnabled": boolean, "sampleRate": number, "maskLevel": string } | null, "tracking_snippet": string } Examples:
Error handling:
|
| umami_get_goalA | Get the conversion rate for a single-step goal: visitors who reached a page, versus all visitors in the same range. A goal is either a page ('path') or a custom event ('event'). Pass exactly one. Umami has no dedicated goals feature, so this is computed by comparing two filtered calls to the stats endpoint. Args:
Returns: JSON shape: { "goal": { "type": "path"|"event", "value": string }, "baseline_visitors": number, "goal_visitors": number, "conversion_rate_pct": number } Examples:
Error handling:
|
| umami_get_funnelA | Get session counts and drop-off across an ordered sequence of pages and/or custom events. Umami has no funnel endpoint, so this walks every session's activity trail in the range (capped by max_sessions) looking for the steps in order. A step matches a page path or a custom event name, whichever it equals; a session only advances once it has completed the previous step. Args:
Returns: JSON shape: { "steps": [ { "step": string, "sessions": number, "pct_of_first": number, "pct_of_previous": number } ], "scanned_sessions": number, "total_sessions_in_range": number, "truncated": boolean } Examples:
Error handling:
|
| umami_get_journeysA | Get the most common sequences of pages visitors take through the site. Umami has no journey/path-analysis endpoint, so this walks every session's activity trail in the range (capped by max_sessions), reduces each to its ordered page paths (consecutive repeats collapsed), truncates to 'depth' steps, and ranks the most frequent sequences. Args:
Returns: JSON shape: { "sequences": [ { "path": string, "sessions": number, "pct": number } ], "matched_sessions": number, "scanned_sessions": number, "total_sessions_in_range": number, "truncated": boolean } Examples:
|
| umami_list_replaysA | List recorded session replays for a website over a date range, newest first. Replays only exist where recording is enabled (umami_get_recorder_config) and a session was sampled. Use umami_get_replay to inspect one in detail. Args:
Returns: JSON shape: { "total": number, "page": number, "page_size": number, "replays": [ { "id": string, "session_id": string, "device": string, "browser": string, "os": string, "country": string, "duration_seconds": number, "event_count": number, "started_at": string } ] } Error handling:
|
| umami_get_replayA | Summarize one recorded session replay: pages visited, click count, and a duration/event breakdown. This does not return the raw rrweb event stream (it can be tens of thousands of events); it summarizes it. Get replay IDs from umami_list_replays with response_format='json'. Args:
Returns: JSON shape: { "session_id": string, "pages": [ { "href": string, "at": string } ], "click_count": number, "clicks": [ { "x": number, "y": number, "pathname": string } ] | undefined } |
| umami_get_click_heatmapA | Get a click-density heatmap for one page path, built from recorded session replays. Umami has no dedicated heatmap endpoint. Click coordinates are captured inside session replay recordings, so this filters replays to the given path, downloads them (capped by max_replays), extracts every click's (x, y) position, normalizes it against that recording's viewport size, and buckets it into a grid. Args:
Returns: JSON shape: { "path": string, "sample_replays": number, "replays_with_clicks": number, "total_clicks": number, "grid_size": number, "cells": [ { "row": number, "col": number, "x_pct_range": [number, number], "y_pct_range": [number, number], "clicks": number } ] } Error handling:
|
| umami_get_retentionA | Get a cohort retention curve: of the distinct visitors seen in the first period, what percentage returned in each period since. Umami has no retention endpoint. This is built from umami.identify()'d visitors: it groups the 'distinctId' metric dimension by period and measures overlap between the earliest period's cohort and each later period. Requires the site to call umami.identify(persistentId) with a stable, persistent ID (e.g. a long-lived cookie or logged-in user ID). Without that, every session has a null distinctId and no cohort can be tracked, this will report zero visitors regardless of real traffic. Args:
Returns: JSON shape: { "cohort_unit": string, "cohort_size": number, "cohort_start": string, "periods": [ { "period": number, "period_start": string, "returning_visitors": number, "retention_pct": number } ] } Error handling:
|
| umami_get_revenueA | Get total and average revenue from a numeric custom-event property, e.g. an 'amount' field on a 'purchase' event. Self-hosted Umami has no built-in revenue tracking. This works by reading the distribution of a numeric property recorded on a custom event, via umami.track(event_name, { [property]: amount }). It sums (value x occurrence count) across every recorded value. Args:
Returns: JSON shape: { "event": string, "property": string, "total_revenue": number, "transaction_count": number, "average_value": number } Error handling:
|
| umami_create_goalA | Create a persisted Goal that appears under Behavior > Goals in the Umami web UI, not just a computed result. Unlike umami_get_goal (which computes a conversion rate on demand and shows nothing in the UI), this saves the goal definition so it shows up for anyone browsing the dashboard. Args:
Returns: { "id": string, "name": string, "type": "goal", "parameters": object } Error handling:
|
| umami_create_funnelA | Create a persisted Funnel that appears under Behavior > Funnels in the Umami web UI, not just a computed result. Unlike umami_get_funnel (which computes step conversion on demand and shows nothing in the UI), this saves the funnel definition so it shows up for anyone browsing the dashboard. Args:
Returns: { "id": string, "name": string, "type": "funnel", "parameters": object } Examples:
|
| umami_list_saved_reportsA | List the saved reports of one type for a website, as they appear in the Umami UI sidebar (Goals, Funnels, Journeys, Retention). For Segments or Cohorts, use umami_list_segments_cohorts instead — they live on a different endpoint. Args:
Returns: JSON shape: { "reports": [ { "id": string, "name": string, "type": string, "parameters": object, "created_at": string } ] } |
| umami_delete_saved_reportA | Permanently delete a saved report (goal, funnel, journey, or retention) so it no longer appears in the Umami UI. Get the report ID from umami_list_saved_reports. For a segment or cohort, use umami_delete_segment_cohort instead. This cannot be undone. Requires confirm=true. Args:
Returns: { "ok": true, "report_id": string } |
| umami_create_segmentA | Create a persisted audience Segment (a saved filter combination) that appears under Audience > Segments in the Umami UI. Args:
Returns: { "id": string, "name": string, "type": "segment", "parameters": object } Examples:
|
| umami_create_cohortA | Create a persisted audience Cohort (visitors who performed an action within a date range, optionally filtered further) that appears under Audience > Cohorts in the Umami UI. Args:
Returns: { "id": string, "name": string, "type": "cohort", "parameters": object } Examples:
|
| umami_list_segments_cohortsA | List the saved Segments or Cohorts for a website, as they appear under Audience in the Umami UI. Args:
Returns: JSON shape: { "reports": [ { "id": string, "name": string, "type": string, "parameters": object, "created_at": string } ] } |
| umami_delete_segment_cohortA | Permanently delete a saved Segment or Cohort so it no longer appears in the Umami UI. Get the ID from umami_list_segments_cohorts. This cannot be undone. Requires confirm=true. Args:
Returns: { "ok": true, "report_id": string } |
| umami_api_getA | Make a read-only GET request against any Umami API endpoint that does not have a dedicated tool here. Use this only as a fallback. The dedicated tools handle date parsing, website resolution, and formatting; this one does not. It is the right choice for endpoints such as /websites/:id/sessions/weekly, /websites/:id/session-data/properties, /websites/:id/session-data/values, /reports, /teams, /me, and anything added in a newer Umami release. Timestamps in params must be epoch milliseconds, and website IDs must be UUIDs. Only GET is permitted, so this tool cannot create, update, or delete anything. Args:
Returns: The raw JSON response from Umami, pretty-printed. Examples:
Error handling:
|
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 48 tools
Every tool targets a distinct resource+action pair, and potentially confusing pairs are explicitly cross-referenced (e.g. umami_get_goal vs umami_create_goal, umami_reset_website vs umami_delete_website, umami_list_saved_reports vs umami_list_segments_cohorts). Descriptions consistently explain when to prefer one tool over an overlapping alternative, so an agent can reliably select the right one.
The dominant umami_<verb>_<noun> pattern in snake_case is followed by the vast majority of tools (list/get/create/update/delete/reset/join/add/remove). Minor deviations exist: umami_traffic_report lacks a verb, and umami_api_get reverses the pattern to noun_verb, but these are isolated and still readable.
48 tools is heavy and pushes well beyond the 15-25 range where a tool set starts to feel bloated. However, the surface genuinely spans websites, teams, users, core analytics, replays, derived analytics, and persisted reports, so most tools earn their place; the count is defensible but will strain agent navigation.
The domain is covered impressively: full CRUD for websites/teams/users, comprehensive analytics querying, replay inspection, computed conversion/funnel/journey/retention/revenue analytics, and persistence for goals, funnels, segments, and cohorts, plus a read-only API fallback. Notable gaps remain: websites cannot be moved between teams (yet umami_delete_team tells you to reassign them first), and saved reports/segments/cohorts have no update path.