# MCP tool reference

## Reporting (`franchisesee-reporting`)

Server URL: `https://app.franchisesee.com/mcp/reporting`

### `compare-districts`

Compare ONE metric across the districts in a district group — the tenant's own grouping (e.g. "Region" or "Market") — over a date range, ranked best to worst. Each district's value is aggregated across the locations in it (summed for summable metrics like net_sales_amount; otherwise the unweighted typical-location mean, including ratio metrics like labor_percentage), so this answers "compare typical location labor % across my districts/regions" in a single call. Defaults to the tenant's primary district group; pass district_group to choose another (call get-tenant-context for the available group names). A summed value also counts locations that have since closed, for the days they traded in the range (closed_num_locations of them, beyond num_locations); a mean covers open locations only. Franchisee tokens are scoped to their own locations by default, so only districts that contain their locations appear, valued over those locations alone; pass whole_districts=true to compare WHOLE districts instead (every shared-leaderboard store in each, as aggregates only — no store is named), valued over open stores, with includes_your_stores marking theirs; a district with fewer than five stores outside the caller's own, or one that would single out such a district by subtraction from the network, is withheld (value null, withheld true: say so, never estimate it). Percentage metrics are ratios (e.g. 0.2 = 20%). Use list-available-metrics for valid metric keys.

- `metric` (string, required): Metric key (e.g., net_sales_amount, labor_percentage, score)
- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `district_group` (string | null): Which district grouping to compare across, by the tenant's own name (e.g. "Region", "Market"). Defaults to the primary group.
- `source` (string | null): Data source (only for rating and audit_score): google, yelp, store_audit, ecosure, smg, meazureup
- `whole_districts` (boolean | null): Franchisee tokens: value each district over all of its stores rather than only the caller's own, to compare their districts with the rest. Franchisor tokens always see whole districts.

### `compare-item-mix`

How the menu ITEM mix shifted between a date range and its comparison: benchmark=prior (default; the adjacent range of the same length) or yoy (the same weekdays 52 weeks earlier). Returns `gainers` and `decliners` ranked by the change in net sales, `new_items` (sold now, not before) and `dropped_items` (sold before, not now), each with net_sales, previous_net_sales, change, change_rate, share and previous_share of the item total. `window` names both date ranges. Pass "category" to compare within one business line (group-first). `top` limits each list (default 20, max 50). Pass "daypart" (a name from get-daypart-mix) to compare within one daypart in both windows, each item counted toward the daypart its check was opened in; accounts without dayparts get an error. Top-level items only; service charges excluded. Franchisee tokens are scoped to their authorized locations.

- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `benchmark` (string | null): prior (default) or yoy
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for network-wide.
- `category` (string | null): Optional category or group name to compare within. Resolved group-first, then a single category.
- `top` (integer | null): Items per list (default 20, max 50)
- `daypart` (string | null): Optional daypart (its name or key, e.g. "Breakfast", from get-daypart-mix) to read within: figures count only checks opened in that daypart, and check penetration is over that daypart's checks. Only for accounts with dayparts configured.

### `compare-locations`

Compare several locations side-by-side across one or more metrics for a date range. Returns a value per location per metric (null where there is no data). Percentage metrics (labor_percentage, cogs_percentage, turnover) are ratios (e.g. 0.2 = 20%). Identify each location by UUID, external_id, or name; every location must be accessible to your token. Use list-available-metrics for valid metric keys.

- `locations` (array, required): Location identifiers (UUID, external_id, or name) to compare
- `metrics` (array, required): Metric keys to compare (e.g., net_sales_amount, labor_percentage, score)
- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `source` (string | null): Data source (only for rating, audit_score metrics): google, yelp, store_audit, ecosure, smg, meazureup, restaurant365

### `get-audit-compliance-report`

Store audit scores and compliance for a single location or network-wide, in one call — enumerating each audit source (EcoSure, MeazureUp, …) and compliance source (Restaurant365, …) the tenant has enabled. Audit scores are averaged over the date range; compliance is the current compliant share (a ratio, e.g. 0.8 = 80% of locations compliant) and ignores the date range, since compliance is a current-state flag. Omit "location" for a network report; franchisee tokens are automatically scoped to their authorized locations.

- `start_date` (string, required): Start date (YYYY-MM-DD) — applies to audit scores
- `end_date` (string, required): End date (YYYY-MM-DD) — applies to audit scores
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for a network-wide report.

### `get-basket-metrics`

Basket shape for a single location or network-wide over a date range, from check-level POS data: checks, net_sales, avg_check, avg_items_per_check (top-level items, not modifiers), avg_guests_per_check (null when the POS reports no guest counts), and check_size_distribution — the number and share of checks under $5, $5–$10, $10–$20, $20–$50 and $50 and over (by check net sales). Pass group_by (day, week, month) for a `series` of the same figures per period. Franchisee tokens are scoped to their authorized locations.

- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for network-wide.
- `group_by` (string | null): Optional series grain: day, week, or month

### `get-channel-mix`

Sales and transaction split by order CHANNEL (e.g. delivery, pickup) and order TYPE (e.g. dine-in, takeout) for a single location or network-wide over a date range, each ranked by net sales. Built from the retained `sales_by_channel` and `sales_by_type` digest metadata. For item-level and per-check questions use get-top-items, get-item-performance, compare-item-mix and get-basket-metrics. Returns empty lists if the tenant has not populated channel/type metadata. A network mix covers open locations only, while a network `net_sales_amount` total also counts closed (deactivated) locations for the days they traded, so channel or type sales need not add up to it. Omit "location" for a network report; franchisee tokens are automatically scoped to their authorized locations.

- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for a network-wide report.

### `get-cogs-report`

Cost-of-goods-sold bundle for a single location or network-wide over a date range, in one call: total COGS, COGS percentage (a ratio, e.g. 0.3 = 30%, weighted by sales across the range), and COGS per customer. Omit "location" for a network report; franchisee tokens are automatically scoped to their authorized locations.

- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for a network-wide report.

### `get-competition-standings`

Ranked standings for the tenant's current competition in one call: the competition window (start/end dates), the competition metric, and each location ranked by value, including the extra per-competition value slots that the generic leaderboard omits. What each slot means is competition-specific — read value_labels in the response for the meaning of value, value_2, and value_3. A convenience over get-leaderboard("competition:<slug>") — no need to know the competition slug. Franchisee tokens see only their authorized locations; franchisors see the whole tenant. Requires the Competition feature.


### `get-custom-metric-report`

Values for this tenant's custom metrics for a single location or network-wide over a date range, in one call — enumerating every custom metric the tenant has configured, with its display label resolved. Omit "location" for a network report; franchisee tokens are automatically scoped to their authorized locations. Pass "metric" to limit the report to one custom metric key (e.g. custom:loyalty_signups).

- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for a network-wide report.
- `metric` (string | null): Optional custom metric key (e.g. custom:loyalty_signups) to report just one metric

### `get-daypart-mix`

Sales split by the tenant's DAYPARTS (its named parts of the trading day, e.g. Breakfast, Lunch, Dinner) for a single location or network-wide over a date range, in trading-day order: net sales, transactions, customers and share of net per daypart, each with its window ("11:00 AM – 2:00 PM", or "Varies by day"). "Outside dayparts" holds sales that could not be placed on the clock. Where labor is split too, each daypart adds labor_minutes and sales_per_labor_hour, counted over only the store-days carrying both splits (labor_coverage says how many), so quote SPLH with that coverage and never divide the sales figures by labor_minutes yourself. Daypart labor is fixed when the day is built, so on an older range where jobs were since marked salaried its minutes can exceed get-labor-report hours. Store-days with no daypart split (dayparts set up partway through the range) are left out, not read as zero; coverage says how many store-days of the range the figures cover. Covers open locations only (sales_per_labor_hour is null for Outside dayparts, whose sales and labor are unrelated). Returns a message and no rows when the tenant has no dayparts configured. Omit "location" for a network report; franchisee tokens are automatically scoped to their authorized locations.

- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for a network-wide report.

### `get-goal-progress`

Goals: targets on a metric (net sales budget, labor % ceiling, average check, rating level, …) for a set of stores over a span split into checkpoints (e.g. fiscal periods). Returns each live goal's current checkpoint, the rolled-up actual vs target, the projected finish, plain pace sentences (quote them: "To catch up, average $1,830 a day for the last 11 days"), the status split (ahead / on track / at risk / behind / too early — always mention it beside a roll-up, because an average hides a failing store), and per-store rows worst first. Status is graded on the projected finish against a seasonal curve from last year, never calendar time. Pass goal (name or id) for one goal, location for one store, include_closed for finished goals. Franchisee tokens see only goals shared with them and only their own stores, plus an anonymized network_context line. Requires the Goals feature.

- `goal` (string | null): Goal name (partial match) or id — omit for every live goal
- `location` (string | null): Location identifier (UUID, external_id, or name) — omit for every store in scope
- `include_closed` (boolean | null): Also return goals whose span has finished (graded hit or missed)

### `get-insights`

Retrieve AI-generated insights about location or network performance. Filter by location, insight type, tier, or delivery status.

- `location` (string | null): Location identifier (UUID, external_id, or name) — omit for network-level insights
- `insight_type` (string | null): Filter by type: daily_sales_summary, daily_labor_summary, daily_metric_summary, network_overview, anomaly_alert, score_driver_breakdown, score_improvement_plan
- `tier` (integer | null): Filter by tier (1=descriptive, 2=diagnostic, 4=prescriptive)
- `undelivered_only` (boolean | null): Only return undelivered insights
- `limit` (integer | null): Max results to return (default 10)

### `get-item-affinity`

What gets added to one menu ITEM (#880): its modifiers ranked by attach rate — the share of the item's lines that carried the modifier at least once (two bowls on a check, one with avocado = 50%). `kind` is "add-on" for POS systems whose modifiers include paid up-sells (Brink) and "option" where they are mostly $0 choices and combo parts such as a drink flavour (Qu) — word the answer accordingly. Each modifier carries attached_lines, net_sales, priced_lines (lines that were charged for; 0 = a free choice), net_per_attach, stores_attaching and `network_average_attach_rate` (the average store's rate across the whole network, a store that never attached it counting as 0 and stores with under 30 lines of the item left out — anonymised, so franchisee tokens get it too; null for them when fewer than five other stores stand behind it). Pass `modifier` (its POS external id or name) on a network call with a franchisor token for `by_location`: every store that sold the item with its attach_rate, attach_gap (store minus network average, ratio points; negative = under-attaching), low_volume, and gap_net_sales_estimate (an estimate of the modifier sales the missing attaches would have brought; null at or above average and for a free modifier — present it as an estimate, never a forecast). Identify the item by its POS external id (from get-top-items) or its name. Franchisee tokens are scoped to their authorized locations and never receive other stores.

- `item` (string, required): The item: its POS external id or its name
- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for network-wide.
- `modifier` (string | null): Optional: one modifier of the item (POS external id or name) to rank stores by. Franchisor tokens, network calls only.
- `top` (integer | null): How many modifiers to return (default 15, max 50)

### `get-item-performance`

How one menu ITEM sold over a date range: totals (net_sales, quantity, checks, check_penetration = share of checks that included it, avg_unit_price), a trend by day, week or month, `network_average_penetration` (the average store's check penetration across the whole network, a store that never sold the item counting as 0% — anonymised, so franchisee tokens get it too — null for them when fewer than five other stores traded, since so few would give a store away), and — for franchisor tokens on a network call — `by_location`, every store that traded ranked by the item's net sales with its share_of_location_sales, check_penetration, never_sold, penetration_gap (store minus network average, ratio points; negative = under-selling) and gap_net_sales_estimate (an estimate of the sales the missing checks would have brought at the item's average unit price; null at or above average — present it as an estimate, never a forecast). Pass benchmark=prior (the adjacent range of the same length) or yoy (the same weekdays 52 weeks earlier) for deltas. Identify the item by its POS external id (from get-top-items) or its name; an ambiguous name returns the candidates. Pass "daypart" (a name from get-daypart-mix) to read within one daypart: totals, trend, each store's penetration and the network average all count only checks opened in it, and the five-other-stores floor counts only stores that traded in it; accounts without dayparts get an error. Counts top-level sales of the item, not its use as a modifier. Franchisee tokens are scoped to their authorized locations and never receive other stores.

- `item` (string, required): The item: its POS external id or its name
- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for network-wide.
- `group_by` (string | null): Trend grain: day, week (default), or month
- `benchmark` (string | null): none (default), prior, or yoy
- `daypart` (string | null): Optional daypart (its name or key, e.g. "Breakfast", from get-daypart-mix) to read within: figures count only checks opened in that daypart, and check penetration is over that daypart's checks. Only for accounts with dayparts configured.

### `get-labor-report`

Full labor picture for a single location or network-wide over a date range, in one call: labor cost, labor percentage (a ratio, e.g. 0.2 = 20%), sales-per-labor-hour, customers-per-labor-hour, total labor hours, and overtime hours. Omit "location" for a network report; franchisee tokens are automatically scoped to their authorized locations. For a per-metric time series use get-metric-trendline.

- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for a network-wide report.

### `get-leaderboard`

Get a ranked leaderboard for a metric over a time period. Returns all locations ranked by value with optional benchmark comparison (prior period or year-over-year). The previous_total aggregate covers every location that reported in the benchmark window (previous_num_locations of them, including any since closed), while the per-location rows only carry a previous_value for locations ranked in the current window — so summing the rows' previous values will not reproduce previous_total whenever the two counts differ. Totals (total, previous_total) also count closed locations for the days they traded — closed_num_locations and previous_closed_num_locations of them — which never appear as rows; mean and median cover open locations only. With a benchmark, total_change and total_change_rate compare total with previous_total, and num_locations_up / num_locations_down count the rows whose value rose or fell (rows with no previous_value are in neither, and both are null when no row has one; for a lower-is-better metric, up is worse). total_change_rate is not a same-store figure: the two totals cover different store sets (num_locations plus closed_num_locations now, previous_num_locations then), so say so rather than presenting it as like-for-like growth — quote these rather than subtracting totals or counting rows. Use list-available-metrics to see valid metric keys.

- `metric` (string, required): Metric key (e.g., net_sales_amount, labor_percentage, score)
- `period` (string, required): Time period: current, yesterday, last_week, last_month, last_90_days, ytd, last_tenant_period
- `benchmark` (string | null): Benchmark comparison: none (default), prior, yoy
- `source` (string | null): Data source (only for rating and audit_score): google, yelp, store_audit, ecosure, smg, meazureup. Compliance is not a leaderboard metric — use get-audit-compliance-report.
- `district_name` (string | null): Filter to locations in this district (by name)
- `category_name` (string | null): Filter to locations in this category (by name)
- `states` (array | null): Filter to locations in these states/regions
- `hide_corporate` (boolean | null): Exclude corporate-owned locations

### `get-location-daily-metrics`

Get day-by-day operational data for a location over a date range. Returns daily sales, transactions, customers, and labor data for trend analysis. labor_percentage is a ratio (e.g. 0.2 = 20%), consistent with other reporting tools. Identify the location by UUID, external_id, or name.

- `location` (string, required): Location identifier (UUID, external_id, or name)
- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `include_labor` (boolean | null): Include labor data (default true)

### `get-location-performance`

Get a single location's metric value, rank, and optional benchmark comparison. Identify the location by UUID, external_id, or name.

- `location` (string, required): Location identifier (UUID, external_id, or name)
- `metric` (string, required): Metric key (e.g., net_sales_amount, labor_percentage)
- `period` (string, required): Time period: current, yesterday, last_week, last_month, last_90_days, ytd, last_tenant_period
- `benchmark` (string | null): Benchmark comparison: none (default), prior, yoy
- `source` (string | null): Data source (only for rating and audit_score). Compliance is not a leaderboard metric — use get-audit-compliance-report.

### `get-metric-snapshot`

One call returning every enabled metric for a single location over a period — value, rank, network mean/median, and benchmark comparison (prior period by default). Powers a full-location overview without one call per metric. Percentage metrics (labor_percentage, cogs_percentage, turnover) are ratios (e.g. 0.2 = 20%). Source-based metrics (ratings, audits) are excluded — use get-location-performance or get-metric-trendline with a source for those. Identify the location by UUID, external_id, or name.

- `location` (string, required): Location identifier (UUID, external_id, or name)
- `period` (string, required): Time period: current, yesterday, last_week, last_month, last_90_days, ytd, last_tenant_period
- `benchmark` (string | null): Benchmark comparison: prior (default), yoy, none

### `get-metric-trendline`

Time series for any metric over a date range — for a single location or network-wide. Group by day (default), week (tenant week-start aware), or month. The response includes range_value: the exact figure for the range — a total or weighted average across it, or for point-in-time metrics the standing as of the end date, per range_aggregation — ALWAYS use it for any whole-range figure instead of summing or averaging the points yourself. Percentage metrics (labor_percentage, cogs_percentage, turnover) are ratios (e.g. 0.2 = 20%). Omit "location" for a network trendline; franchisee tokens are automatically scoped to their authorized locations. Report-based metrics (cogs, retention) return one point per report period regardless of grouping. Competition metrics return a cumulative running-total curve truncated to the competition window — points are standings, never add them together — plus a competition block naming the resolved competition and its window. To set the trend beside other stores, pass compare_to (+ compare_to_name) for ONE group — the whole network, a district, a state or a category — and the response adds comparison_group.points: the TYPICAL STORE in that group per bucket (mean and median of each store's value for the bucket, e.g. one store's weekly sales), on the same buckets — compare a single location's points with it directly (for one store, each point also carries yours and gap = yours − mean, and gap_summary counts the buckets it beat the typical store in and names the closest and widest — quote these rather than reading the points), but divide a multi-location network total by its store count first. A bucket with too few stores outside the caller's own (or one that would single out a smaller group by subtraction) is withheld: say so, never fill it in. At most 26 buckets — group by week or month for longer ranges; not available for competition metrics. Use list-available-metrics for valid metric keys.

- `metric` (string, required): Metric key (e.g., net_sales_amount, labor_percentage, score)
- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for a network-wide trendline.
- `grouping` (string | null): Time bucket: day (default), week, month
- `source` (string | null): Data source (only for rating, audit_score metrics): google, yelp, store_audit, ecosure, smg, meazureup, restaurant365
- `compare_to` (string | null): Compare against the stores in ONE group, as aggregates only (store count, mean, median, quartiles, movement; total for franchisor tokens): network (the whole brand), district (either district group), state, or category (e.g. a store format). Works for franchisee tokens too — the group need not contain their own stores — and is withheld when fewer than 5 stores outside the caller's own stand behind it. No store in the group is ever named.
- `compare_to_name` (string | null): The district, state/region or category to compare against. Omit it for a category or state to use the caller's own (e.g. "stores in my format"); a district must be named, and leaving it out returns the caller's own districts to choose from.

### `get-network-leaderboard-summary`

Show where the caller's own location(s) stand within the wider network for one metric: their rank ("14th of 115"), their percentile, and how many network locations improved vs. declined against the benchmark. Returns the caller's own row plus anonymous network aggregates only — never another location's name or value; the app's leaderboard is where named per-store numbers live. Unlike get-leaderboard, which scopes a franchisee to their own locations, this ranks them against the full peer board, so it is the tool to use for "how do I compare to the network / am I above average / how many stores are down". Peer board = the same board the location is ranked against in the app (category partitioning and corporate visibility respected). To compare against a different group — "my district", "stores in Texas", "the GC10 format", "the whole brand" — pass compare_to (+ compare_to_name): the response adds comparison_group (that group's store count, mean, median, quartiles, top_quarter_starts_at = the value a store must beat to be in its best quarter, and movement counts) and each location's rank or percentile within it, even when the location is not in that group. A group is withheld when fewer than five stores outside the caller's own stand behind it, or when it would single out a smaller withheld group by subtraction: say so, never estimate it.

- `metric` (string, required): Metric key (e.g., net_sales_amount, labor_percentage, score). Use list-available-metrics for the keys this tenant supports.
- `period` (string, required): Time period: current, yesterday, last_week, last_month, last_90_days, ytd, last_tenant_period
- `benchmark` (string | null): Which comparison the improved/declined counts are measured against: prior (default) or yoy. Not every tenant has a year of history — when yoy resolves to nothing the response says so rather than reporting zero movement.
- `source` (string | null): Data source (only for rating and audit_score): google, yelp, store_audit, ecosure, smg, meazureup
- `location_name` (string | null): Which of the caller's own locations to report standing for. Optional for a franchisee — omit it to cover every location they operate. Franchisors must name one.
- `compare_to` (string | null): Compare against the stores in ONE group, as aggregates only (store count, mean, median, quartiles, movement; total for franchisor tokens): network (the whole brand), district (either district group), state, or category (e.g. a store format). Works for franchisee tokens too — the group need not contain their own stores — and is withheld when fewer than 5 stores outside the caller's own stand behind it. No store in the group is ever named.
- `compare_to_name` (string | null): The district, state/region or category to compare against. Omit it for a category or state to use the caller's own (e.g. "stores in my format"); a district must be named, and leaving it out returns the caller's own districts to choose from.

### `get-network-metric-summary`

Aggregate one metric across all (or filtered) locations for an arbitrary date range. Returns the network distribution — total (summable metrics only), typical-location mean, median, and the count of locations with data — plus a short ranked preview. The total also counts locations that have since closed, for the days they traded in the range (closed_num_locations of them, beyond num_locations); mean and median cover open locations only. Digest-backed ratio metrics also return weighted_mean, the network treated as one business (sum numerator / sum denominator). Percentage metrics (labor_percentage, cogs_percentage, turnover) are ratios (e.g. 0.2 = 20%). Use list-available-metrics for valid metric keys. Franchisee tokens are automatically scoped to their authorized locations; to compare those against other stores, pass compare_to (+ compare_to_name) for ONE group — the whole network, a district, a state or a category — and the response adds comparison_group: that group's store count, mean, weighted_mean, median, quartiles and top_quarter_starts_at (the value a store must beat to be in its best quarter), plus total for franchisor tokens, never a store row. A group is withheld when fewer than five stores outside the caller's own stand behind it, or when it would single out a smaller withheld group by subtraction: say so, never estimate it.

- `metric` (string, required): Metric key (e.g., net_sales_amount, labor_percentage, score)
- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `source` (string | null): Data source (only for rating and audit_score): google, yelp, store_audit, ecosure, smg, meazureup. Compliance is not a leaderboard metric — use get-audit-compliance-report.
- `district_name` (string | null): Filter to locations in this district (by name)
- `category_name` (string | null): Filter to locations in this category (by name)
- `states` (array | null): Filter to locations in these states/regions
- `hide_corporate` (boolean | null): Exclude corporate-owned locations
- `top` (integer | null): How many top-ranked locations to include in the preview (default 5)
- `compare_to` (string | null): Compare against the stores in ONE group, as aggregates only (store count, mean, median, quartiles, movement; total for franchisor tokens): network (the whole brand), district (either district group), state, or category (e.g. a store format). Works for franchisee tokens too — the group need not contain their own stores — and is withheld when fewer than 5 stores outside the caller's own stand behind it. No store in the group is ever named.
- `compare_to_name` (string | null): The district, state/region or category to compare against. Omit it for a category or state to use the caller's own (e.g. "stores in my format"); a district must be named, and leaving it out returns the caller's own districts to choose from.

### `get-product-mix`

Sales and quantity split by product CATEGORY for a single location or network-wide over a date range, ranked by net sales. Built from the retained `sales_by_category` digest metadata. Each category row carries its stable `category_uuid` (null for uncategorized or deleted-category sales) and its `group` — the tenant-assigned business line it rolls up into (null when ungrouped). Pass "category" to focus the report on one line: it resolves group-first (a tenant group such as "Meal Plans") and falls back to a single category of that name, and the response echoes the match under `filter` with that line's net, quantity, and share of the whole mix. For individual menu items use the item-level tools: get-top-items (best sellers), get-item-performance (one item over time and across stores), compare-item-mix (what grew, declined, appeared or disappeared) and get-basket-metrics (checks, items and guests per check). The returned `total_net_sales` is the sum of category item sales (the basis for each category's share) and can differ from the `net_sales_amount` metric, which is order-level and nets in refunds, comps, and other adjustments not attributed to a category. A network mix also covers open locations only, while a network `net_sales_amount` total also counts closed (deactivated) locations for the days they traded, so over a range in which a location closed the gap includes that location's sales. Returns empty if the tenant has not populated category metadata. Omit "location" for a network report; franchisee tokens are automatically scoped to their authorized locations.

- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for a network-wide report.
- `category` (string | null): Optional category or group name to focus on. Resolved group-first (a tenant business line such as "Meal Plans"), then falling back to a single category of that name.
- `top` (integer | null): Limit to the top N categories by net sales (default: all)

### `get-ratings-report`

Average customer rating per source (Google, Yelp, …) for a single location or network-wide over a date range, in one call — enumerating exactly the rating sources this tenant has enabled. Google and Yelp publish only a lifetime rating — the all-time average of every review a location has ever received, re-read weekly — so their average here is the mean of the lifetime readings taken in the range, not a rating earned during it; those sources are marked is_lifetime_average with a note saying so. Omit "location" for a network report; franchisee tokens are automatically scoped to their authorized locations. For a per-source time series use get-metric-trendline with metric=rating and a source.

- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for a network-wide report.

### `get-retention-report`

Employee turnover bundle for a single location or network-wide over a date range, in one call: manager turnover rate and non-manager turnover rate (both ratios, e.g. 0.25 = 25%, averaged across retention reports in the range). Omit "location" for a network report; franchisee tokens are automatically scoped to their authorized locations.

- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for a network-wide report.

### `get-risk-findings`

Risk Assessment flags: stores whose discount rate or $0-check rate is outside their usual range — and, where the account scores it, whose cash share of payments has fallen well below its own norm for weeks (a low cash share can mean cash sales not rung up; it can also mean a new kiosk or payment habit) — and why. A "flag" is a risk finding. Each finding carries factors — plain sentences stating the evidence (the store against its own history, against comparable stores, trend, the other rate, network moves, check volume); explain a flag by quoting them rather than inferring a cause, and never present a flag as evidence of wrongdoing. By default returns unresolved findings (open or acknowledged), worst tier first and longest-running first within a tier; weeks_open counts the opening week as week 1, and total is the count before the limit. summary carries precomputed counts over every matching finding, before the limit: findings and distinct stores overall, per metric (by_metric, each with by_tier) and per tier (by_tier), and stores_on_both_metrics (stores flagged on more than one rate) with their names. Quote these for any count, heading, or "flagged on both" list — never count or group the rows yourself, and say when findings were cut by the limit. Pass location for one store (the response then adds latest_assessment for each rate the account scores). Rates are percents. Franchisee tokens see only their own stores' findings, without tier names, and never network findings; include_network (franchisor only) adds network_findings, where much of the network moved together. Requires the Risk Assessment feature.

- `location` (string | null): Location identifier (UUID, external_id, or name) — omit for every store in scope
- `metric` (string | null): Only this rate: discount_rate, zero_net_check_rate, or cash_share (only where the account scores cash share)
- `status` (string | null): open, acknowledged, explained, cleared, or any. Default: open and acknowledged (still unresolved)
- `min_tier` (string | null): Franchisor only: watch, elevated, or high — findings at or above this tier
- `opened_since` (string | null): Only findings opened on or after this date (YYYY-MM-DD)
- `include_network` (boolean | null): Franchisor only: also return network-wide shift findings
- `limit` (integer | null): Max findings to return (default 25, max 100)

### `get-score-drivers`

Explain WHY a single location's SEEScore is what it is — the per-metric breakdown that produced it. Returns each weighted component metric with its impact weight, raw 0–10 performance multiplier and tier (poor/adequate/excellent), the scoring curve used (linear/step), the graded value against its target band (low_lt/high_gt, when available), earned vs. max points, and point gap (improvement opportunity), plus the location's last 4 weeks of weekly score trend. Use this when the composite score (from get-leaderboard / get-metric-snapshot / get-metric-trendline) isn't enough and you need to know which metrics are dragging the score down or holding it up. Single location only. Identify the location by UUID, external_id, or name.

- `location` (string, required): Location identifier (UUID, external_id, or name)

### `get-tenant-context`

Get tenant configuration including district group names, category group names, and feature settings. Call this first to learn the tenant vocabulary.


### `get-tender-mix`

How guests paid (#882): payments by tender over a date range, from check-level POS data — the same figures as the Tender card on Sales Analytics. `tenders` lists each tender (its POS name, e.g. "Visa", "Cash", "DoorDash") with payments, amount, share (of the payment amount; Brink refund lines are netted in, Qu refunds are not recorded yet), avg_payment and tip_amount. `types` groups them by the POS's own tender category ("Credit card", "External", "Cash" on Brink; "Credit", "Offline Credit", "Gift Card" on Qu — present them as the POS names them; Qu's "Offline Credit" mixes cards with delivery, catering and A/R, so `types_note` says so). `cash_share` is the scope's cash share of payments and `network_average_cash_share` the average store's across the network (stores with under 30 payments left out; anonymised, so franchisee tokens get it too, null for them when fewer than five other stores stand behind it). `card_brands` splits card payments by brand where the POS records one (Qu); on Brink the card tenders are the brands themselves, so it is empty. Shares are of payments taken, which run a little under check totals: never call them a share of sales. Pass `by_location: true` for each store's total and split by type (the stores in scope only). Franchisee tokens are scoped to their authorized locations and never receive other stores.

- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for network-wide.
- `by_location` (boolean | null): Include each store's total and split by tender type (default false)
- `top` (integer | null): How many tenders to list (default 25, max 50)

### `get-top-items`

Best-selling menu ITEMS for a single location or network-wide over a date range, from check-level POS data. Rank by net sales (default), quantity, checks (how many checks included the item), or voids (voided units — what gets taken off checks most). Each row carries the POS `item_external_id` (pass it to get-item-performance), the item name, its category and group, net_sales, quantity, checks, check_penetration (share of all checks in scope that included the item), avg_unit_price (net per unit), share of the item total, and void_quantity / void_amount (voided units and their value; voided lines are never in the sales figures; a 0 void_amount beside voided units means the POS sent no price on the voided lines, so report the units, not "$0 voided"). Pass "category" to rank within one business line: it resolves group-first (e.g. "Meal Plans") and falls back to a single category. `top` defaults to 15 (max 50). The list holds only the top items by rank_by, so a superlative holds only for that measure: rows ranked by net_sales do not show which item sold the most units or reached the most checks — call again with rank_by quantity, checks or voids before claiming that, and otherwise say "among these top items". `groups` splits the listed items by group (category when ungrouped) with the item count, net sales and share of each — quote it for "how many of these are X" rather than counting rows. Top-level lines only unless include_modifiers is true (then its modifier sales add in too, and its checks are the larger of the two counts, since one check can hold both); the parts and add-ons of a Qu combo are modifier lines. Pass "daypart" (a name from get-daypart-mix, e.g. Breakfast) to rank within one daypart: an item counts toward the daypart its check was opened in, and check_penetration is then the share of that daypart's checks; accounts without dayparts get an error. Service charges and lines that never carried revenue (order notes, loyalty markers) are excluded; an item that was only voided is kept. `total_net_sales` is the item total in scope — it can differ from the order-level net_sales_amount metric. A `data_from` note means the range starts before item data exists; a `message` means item data is not available for this account. Franchisee tokens are scoped to their authorized locations.

- `start_date` (string, required): Start date (YYYY-MM-DD)
- `end_date` (string, required): End date (YYYY-MM-DD)
- `location` (string | null): Optional location identifier (UUID, external_id, or name). Omit for a network-wide ranking.
- `category` (string | null): Optional category or group name to rank within. Resolved group-first, then a single category.
- `rank_by` (string | null): net_sales (default), quantity, checks, or voids
- `top` (integer | null): How many items to return (default 15, max 50)
- `include_modifiers` (boolean | null): Include modifiers and add-on lines as items (default false)
- `daypart` (string | null): Optional daypart (its name or key, e.g. "Breakfast", from get-daypart-mix) to read within: figures count only checks opened in that daypart, and check penetration is over that daypart's checks. Only for accounts with dayparts configured.

### `list-available-metrics`

List all metrics enabled for this tenant, including display names, types (currency/percentage), and behavior (lower-is-better). Use the metric key values when calling leaderboard tools.


### `report-tool-issue`

Record that these tools could not answer a request, so the FranchiseSEE team can build the missing reports. It applies when no tool covers the request, when a call succeeded but did not have the intended effect (for example it returned nothing where data should exist), when a tool kept failing, or when no accepted argument could be worked out. Describe the gap in terms of reports, metrics, stores and dates. Do not quote the user, and leave out names, contact details and other personal information. It changes no store data.

- `category` (string, required): What kind of problem: capability_gap (no tool exists for what the user asked), unexpected_result (a call succeeded but did not have the effect you intended), tool_failure (a tool errored or kept erroring), invalid_argument (you could not work out an argument the tool would accept), other
- `tool` (string | null): The exact name of the tool involved, if there is one — for example "update-location". Leave empty for a capability gap: that is the case where no tool exists.
- `goal` (string, required): The report, metric, stores and period that were needed — for example "labor percentage by daypart for one location, last month". Do not quote the user or include names or other personal details.
- `details` (string, required): What happened and what was needed instead. If arguments did not work, say which. No personal details.

## Admin (`franchisesee-admin`)

Server URL: `https://app.franchisesee.com/mcp/admin`

### `add-location-groups-for-user`

Add location groups to a user, appending to their existing groups (unlike sync-location-groups-for-user, which replaces all groups). A user's access is either a single direct location OR location groups — so assigning groups clears any direct location assignment. Requires MANAGE_USERS permission.

- `identifier` (string, required): User identifier (email or ID)
- `location_group_identifiers` (array, required): Array of location group identifiers (UUID, external_id, or name) to add

### `bulk-add-location-groups-for-users`

Add location groups to multiple users in a single operation. Each entry specifies a user (by email) and location groups to add.
Groups are appended to existing assignments — existing group memberships are preserved.
A user given groups loses their direct location: access is one direct location or groups, never both.
Ideal for processing CSV-style bulk user-to-group assignments.

- `assignments` (array, required): Array of {email, location_groups: [identifier, ...]} pairs. Each location_group can be a UUID, external_id, or name.

### `bulk-assign-locations-to-district`

Assign multiple locations to a district in a single operation. Each entry specifies a location and a district.
The district_group_name determines which district slot to update (e.g., "Region" → district_id, "Market" → district_id_2).
Use get-tenant-context to see the tenant's district group names. Ideal for processing CSV-style bulk reassignments.

- `district_group_name` (string, required): District group name (e.g., "Region", "Market") — determines which district slot to update
- `assignments` (array, required): Array of {location, district} pairs. Each location/district can be a UUID, external_id, or name.

### `create-district`

Create a new district within a district group. Specify the group by its tenant-customized name. Requires MANAGE_LOCATIONS permission.

- `name` (string, required): District name (required)
- `district_group_name` (string, required): District group name (required) — use get-tenant-context to see available groups
- `external_id` (string | null): External identifier

### `create-location`

Create a new location. Requires MANAGE_LOCATIONS permission.

- `name` (string, required): Location name (required)
- `address` (string | null): Street address
- `city` (string | null): City
- `region` (string | null): State/region
- `postal_code` (string | null): Postal code
- `country` (string | null): Country
- `external_id` (string | null): External identifier (must be unique within tenant)
- `district` (string | null): District identifier (UUID, external_id, or name)
- `district2` (string | null): Secondary district identifier (UUID, external_id, or name)
- `location_category` (string | null): Location category identifier (UUID, external_id, or name)
- `opened_on` (string | null): The first day the location traded, as YYYY-MM-DD. Same-store (comp) comparisons only count a location that was open for the whole comparison window; omit when unknown.
- `is_active` (boolean | null): Activate the location immediately. Omit — or send false — to leave it pending, in which case it collects no metrics and appears on no leaderboard until someone activates it. Where the tenant ranks locations within a category, an active location must be given one from the primary category group.

### `create-location-category`

Create a new location category within a category group. Specify the group by its tenant-customized name. Requires MANAGE_LOCATIONS permission.

- `name` (string, required): Category name (required)
- `category_group_name` (string, required): Category group name (required) — use get-tenant-context to see available groups
- `external_id` (string | null): External identifier
- `sort_order` (integer | null): Sort order (0-based)

### `create-location-group`

Create a new location group. Requires MANAGE_LOCATIONS permission.

- `name` (string, required): Group name (required)
- `external_id` (string | null): External identifier (must be unique within tenant)

### `delete-district`

Delete a district. Cannot delete districts that have locations assigned. Requires MANAGE_LOCATIONS permission.

- `identifier` (string, required): District identifier (UUID, external_id, or name)

### `delete-location`

Permanently delete a location and all its history. Only a location whose is_deletable is true (see list-locations) can be deleted: a pending one, a deactivated one that never loaded any sales, labor or custom-metric data, or a deactivated one whose history has aged out of reporting retention. A closed store keeps counting toward network totals and year-over-year benchmarks until then (deletable_on gives the date), so the call is refused before it. To take a store off leaderboards, deactivate it with update-location instead. Requires MANAGE_LOCATIONS permission.

- `identifier` (string, required): Location identifier (UUID, external_id, or name)

### `delete-location-category`

Delete a location category. Cannot delete categories that have locations assigned. Requires MANAGE_LOCATIONS permission.

- `identifier` (string, required): Category identifier (UUID, external_id, or name)

### `delete-location-group`

Delete a location group. Cannot delete groups that have locations or users assigned. Requires MANAGE_LOCATIONS permission.

- `identifier` (string, required): Location group identifier (UUID, external_id, or name)

### `delete-user`

Delete a user. Identify by email or ID. Requires MANAGE_USERS permission.

- `identifier` (string, required): User identifier (email or ID)

### `get-tenant-context`

Get tenant configuration including district group names, category group names, and feature settings. Call this first to learn the tenant vocabulary.


### `list-available-metrics`

List all metrics enabled for this tenant, including display names, types (currency/percentage), and behavior (lower-is-better). Use the metric key values when calling leaderboard tools.


### `list-districts`

List districts, optionally filtered by district group name or external_id. Include districtGroup to see which group each district belongs to.

- `district_group_name` (string | null): Filter by district group name (tenant-customized)
- `filter_external_id` (string | null): Filter by external_id
- `include` (string | null): Comma-separated includes: districtGroup
- `per_page` (integer | null): Results per page (1–100; default 30)

### `list-location-categories`

List location categories, optionally filtered by category group name or external_id.

- `category_group_name` (string | null): Filter by category group name (tenant-customized)
- `filter_external_id` (string | null): Filter by external_id
- `include` (string | null): Comma-separated includes: locationCategoryGroup
- `per_page` (integer | null): Results per page (1–100; default 30)

### `list-location-groups`

List location groups with optional location includes and external_id filter.

- `filter_external_id` (string | null): Filter by external_id
- `include_locations` (boolean | null): Include the locations in each group
- `per_page` (integer | null): Results per page (1–100; default 30)

### `list-locations`

List locations with optional filters. Supports filtering by external_id and including related district, category, and group data.

- `filter_external_id` (string | null): Filter by external_id
- `include` (string | null): Comma-separated includes: locationCategory, district, district2, locationGroups
- `per_page` (integer | null): Results per page (1–100; defaults to 100). Use page to fetch more.
- `page` (integer | null): Page number
- `sort` (string | null): Sort field: name, created_at, updated_at. Prefix with - for descending.

### `list-users`

List users for the current tenant. Supports filtering by email and perspective (franchisor/franchisee).

- `filter_email` (string | null): Filter by email address
- `filter_perspective` (string | null): Filter by perspective: franchisor, franchisee
- `include` (string | null): Comma-separated includes: locationGroups, location
- `per_page` (integer | null): Results per page (1–100; default 30)
- `sort` (string | null): Sort field: first_name, last_name, email, created_at, updated_at

### `report-tool-issue`

Record that these tools could not carry out a request, so the FranchiseSEE team can build the missing capabilities. It applies when no tool covers the request, when a call succeeded but did not have the intended effect, when a tool kept failing, or when no accepted argument could be worked out. Describe the gap in terms of reports, metrics, stores and dates. Do not quote the user, and leave out names, contact details and other personal information. It changes no store data.

- `category` (string, required): What kind of problem: capability_gap (no tool exists for what the user asked), unexpected_result (a call succeeded but did not have the effect you intended), tool_failure (a tool errored or kept erroring), invalid_argument (you could not work out an argument the tool would accept), other
- `tool` (string | null): The exact name of the tool involved, if there is one — for example "update-location". Leave empty for a capability gap: that is the case where no tool exists.
- `goal` (string, required): The report, metric, stores and period that were needed — for example "labor percentage by daypart for one location, last month". Do not quote the user or include names or other personal details.
- `details` (string, required): What happened and what was needed instead. If arguments did not work, say which. No personal details.

### `sync-location-groups-for-location`

Set which location groups a location belongs to. Replaces all current group assignments. Requires MANAGE_LOCATIONS permission.

- `identifier` (string, required): Location identifier (UUID, external_id, or name)
- `location_group_identifiers` (array, required): Array of location group identifiers (UUID, external_id, or name)

### `sync-location-groups-for-user`

Set which location groups a user belongs to. Replaces all current group assignments and clears the user's direct location assignment. Pass an empty array to remove all of the user's group access (leaving them with no assigned locations). Requires MANAGE_USERS permission.

- `identifier` (string, required): User identifier (email or ID)
- `location_group_identifiers` (array, required): Array of location group identifiers (UUID, external_id, or name)

### `update-district`

Update an existing district. Identify by UUID, external_id, or name. Requires MANAGE_LOCATIONS permission.

- `identifier` (string, required): District identifier (UUID, external_id, or name)
- `name` (string | null): New district name
- `external_id` (string | null): External identifier

### `update-location`

Update an existing location. Identify by UUID, external_id, or name. Requires MANAGE_LOCATIONS permission.

- `identifier` (string, required): Location identifier (UUID, external_id, or name)
- `name` (string | null): New location name
- `address` (string | null): Street address
- `city` (string | null): City
- `region` (string | null): State/region
- `postal_code` (string | null): Postal code
- `country` (string | null): Country
- `external_id` (string | null): External identifier
- `district` (string | null): District identifier (UUID, external_id, or name). Send null to unassign the location from its district; omit to leave it unchanged.
- `district2` (string | null): Secondary district identifier. Send null to unassign; omit to leave it unchanged.
- `location_category` (string | null): Location category identifier (UUID, external_id, or name). Send null to unassign; omit to leave it unchanged.
- `opened_on` (string | null): The first day the location traded, as YYYY-MM-DD. Same-store (comp) comparisons only count a location that was open for the whole comparison window; unknown (null) leaves it to the sales signal alone. Send null to clear; omit to leave it unchanged.
- `is_active` (boolean | null): Activation state. Omit to leave it unchanged. False takes an active location out of service (off leaderboards, kept in historical network totals); true brings it back. False on a location that was never activated leaves it pending rather than deactivated. Where the tenant ranks locations within a category, an activated location must have one from the primary category group.

### `update-location-category`

Update an existing location category. Identify by UUID, external_id, or name. Requires MANAGE_LOCATIONS permission.

- `identifier` (string, required): Category identifier (UUID, external_id, or name)
- `name` (string | null): New category name
- `external_id` (string | null): External identifier
- `sort_order` (integer | null): Sort order (0-based)

### `update-location-group`

Update an existing location group. Identify by UUID, external_id, or name. Requires MANAGE_LOCATIONS permission.

- `identifier` (string, required): Location group identifier (UUID, external_id, or name)
- `name` (string | null): New group name
- `external_id` (string | null): External identifier

### `update-user`

Update an existing user. Identify by email or ID. Requires MANAGE_USERS permission.

- `identifier` (string, required): User identifier (email or ID)
- `first_name` (string | null): First name
- `last_name` (string | null): Last name
- `perspective` (string | null): User perspective: franchisor or franchisee. Can only be set while the user has none (during provisioning) — a set perspective is immutable. To change what kind of user someone is, delete the user and re-invite them.
- `location` (string | null): Assigned location identifier (UUID, external_id, or name) — for franchisee users. Setting a location removes all of the user's location-group assignments (a user has a direct location or groups, never both).
- `roles` (array | null): Array of role names to assign
