← AppstackCONTENT HISTORY

Update to Appstack

Snapshot Nov 7, 2026 · 00:09 UTC · version 1.2.0

Collection source: downloaded plugin package.

WHAT CHANGED

First saved snapshot

No earlier snapshot is available to establish a change.

Save Appstack and check future changes in Following.

Up to 3 plugins free · no card required.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Best practices for using the Appstack Analytics MCP connector efficiently — correct tool call order, exact Cube measure/dimension naming conventions, and how to resolve a company name to app scope. Use whenever connected to the Appstack MCP server and asked for attribution/analytics data, ad spend, installs, revenue, integrations status, onboarding progress, dashboards, user journeys, or to create/manage standard links.",
  "included_files": [
    {
      "relative_path": "agents/openai.yaml",
      "size_in_bytes": 261
    }
  ],
  "name": "appstack-mcp",
  "skill_md_contents": "---\nname: appstack-mcp\ndescription: >-\n  Best practices for using the Appstack Analytics MCP connector efficiently —\n  correct tool call order, exact Cube measure/dimension naming conventions,\n  and how to resolve a company name to app scope. Use whenever connected to\n  the Appstack MCP server and asked for attribution/analytics data, ad spend,\n  installs, revenue, integrations status, onboarding progress, dashboards,\n  user journeys, or to create/manage standard links.\n---\n\n# Using the Appstack Analytics MCP connector well\n\nThe Appstack MCP server exposes real attribution and analytics data — installs,\nad spend, revenue, integrations, dashboards, user journeys, and standard links.\nEvery tool call costs a round trip; guessing instead of discovering wastes\nseveral of them and often fails outright. This skill exists to cut that down\nto as few calls as possible.\n\n**The one rule that matters most: never guess a measure, dimension, or filter\nvalue. Discover it first.** A guessed measure/dimension name fails; a guessed\nfilter value silently returns an empty result, which looks like \"no data\"\nrather than \"wrong input\" — much harder to debug after the fact.\n\n## Always start here\n\nCall `whoami` **first**, before anything else, in every new session. It\nresolves in one call:\n\n- Which app_id(s)/app_name(s) the connection has access to.\n- Each app's own organization_id/organization_name.\n- Whether this is a project-scoped credential (`scope: \"project\"`) or a\n  platform-admin one (`scope: \"all_organizations\"`).\n\nDon't call `list_dimension_values` or guess app scope from a natural-language\ncompany name — `whoami` already has it.\n\n### Resolving a company name (platform-admin scope only)\n\nIf asked about a specific company by name (e.g. \"give me the ad spend for\nCarrots\") under `all_organizations` scope, call `find_organization(\"carrots\")`\n— **do not** scan `whoami`'s full app list by hand looking for a name match.\n`whoami`'s app list for platform-admin scope can run into the hundreds of\napps across unrelated organizations; `find_organization` resolves a\npartial, case-insensitive name match to the actual organization_id and its\napps in one call. Only fall back to scanning if `find_organization` finds\nnothing and you suspect \"Carrots\" might actually be an app name, not a\ncompany name.\n\n## Never guess a Cube measure or dimension name\n\nBefore calling `query_metrics` with a measure/dimension you haven't already\nconfirmed exists, call `list_metrics` (schema) or `list_dimension_values`\n(valid values) first — unless you already have that schema in context from\nearlier in the same session, in which case reuse it instead of re-fetching.\n\n`list_metrics` is two-step: with no argument it returns a compact index of the\navailable views (name, description, measure/dimension counts); call it again\nwith `view=\"<name>\"` to get that view's measures and dimensions. There is more\nthan one view — `events_view` holds ad-network and attribution metrics\n(impressions, clicks, CTR, CPM, spend, installs), `eac_cohorts_view` holds\ncohorts by install date — so never conclude a metric doesn't exist after\nreading a single view.\n\n**Concrete example of the failure mode this prevents:** the install-count\nmeasure is `events_view.install` — **singular**, not `events_view.installs`.\nA plural guess (the more natural English form) fails outright rather than\nbeing auto-corrected. Treat every measure/dimension name as unconfirmed until\n`list_metrics` has actually shown it to you, even ones that seem obvious.\n\nMeasures and dimensions confirmed to exist on `events_view` (there are more —\ncall `list_metrics` for the full set, this list is not exhaustive):\n\n- Measures: `events_view.install`, `events_view.total_spend`,\n  `events_view.revenue`, `events_view.trial_started`,\n  `events_view.in_app_purchase`.\n- Dimensions: `events_view.app_id`, `events_view.app_name`,\n  `events_view.source_type` (e.g. filter `equals` `\"non_organic\"` for\n  ad-attributed activity), `events_view.country`, `events_view.media_source`.\n\nCohorts (`eac_cohorts`/`core_eac_cohorts`) don't need a different tool —\nthey're reachable through the same `list_metrics`/`query_metrics` calls as\nany other Cube view.\n\n## Interpreting ambiguous phrasing\n\nUsers describe attribution in casual terms that don't map 1:1 to a view or\ndimension name. Don't guess which view a phrase means — use this table, and\nwhen a term carries a caveat, say so rather than silently picking a side.\n\n| User says | Usually means | How to query it |\n| --- | --- | --- |\n| \"web installs\" / \"eac installs\" / \"Appstack installs\" | Installs attributed by Appstack's own tracking (EAC) — not the ad network's native app campaign, not Apple Ads | `events_view.install` filtered `events_view.attribution_type = 'appstack'`. **Not** `eac_cohorts_view` by default — see below. |\n| \"organic\" | No attributed media source | `events_view.source_type = 'organic'` (equivalently `attribution_type = 'organic'`) |\n| \"non-organic\" / \"paid\" / \"ad network\" | Anything attributed to a media source, any campaign type | `events_view.source_type = 'non_organic'` |\n| \"Apple Ads\" / \"ASA\" / \"Apple Search Ads\" | Apple's native app-campaign attribution | `events_view.media_source = 'apple'` — this is `attribution_type = 'app'`, never `'appstack'`, so it's never part of an \"eac\" answer |\n\n### `eac_cohorts_view` is not \"the EAC view\" — it always includes Apple Ads\n\n`eac_cohorts_view` is built for cohort/retention analysis (installs bucketed\nby days/weeks/months since install), not as a synonym for \"EAC installs.\" Its\nunderlying model unions three sources: Appstack/EAC-attributed installs,\nApple Ads installs, and standard-link installs — by design, every query\nagainst it includes Apple Ads unless you filter it out.\n\nIf a user asks for \"eac installs\" and either the request needs cohort/\nretention behavior, or you're about to reach for this view just because \"eac\"\nis in its name: filter `media_source != 'apple'` and tell the user you\nexcluded Apple Ads — don't silently include or exclude it, since that changes\nthe number either way.\n\n## Speak in plain language, not Cube internals\n\nMeasure/dimension paths (`events_view.install`), view names\n(`eac_cohorts_view`), filter objects, and tool names are internal\nimplementation details for reasoning about which call to make — not for the\nresponse. Default to describing what was measured and how it was filtered in\nplain business language, e.g. \"EAC-attributed installs, excluding Apple Ads,\"\nnot \"`events_view.install` filtered `attribution_type = 'appstack' AND\nmedia_source != 'apple'`.\"\n\nOnly surface raw Cube member names, tool names, or filter objects when the\nuser explicitly asks (e.g. \"what query did you run,\" \"what filter/dimension\ndid you use,\" \"show me the raw query\"). A few translations to default to:\n\n- `events_view.install` → \"installs\"\n- `events_view.total_spend` → \"ad spend\"\n- `attribution_type = 'appstack'` → \"Appstack-attributed (EAC)\"\n- `source_type = 'organic'` / `'non_organic'` → \"organic\" / \"paid\"\n- `media_source = 'apple'` → \"Apple Ads\"\n\nThis applies to every tool, not just `query_metrics` — a `list_dashboards`/\n`get_dashboard` response's already-resolved `cube_name.field_name` paths, or a\n`list_metrics` schema dump, get translated before reaching the user the same\nway, unless they asked for the schema/query itself.\n\n## Sanity-check results before answering\n\nBefore presenting a `query_metrics` (or cohort) result, scan it for shapes\nthat usually mean something's wrong upstream — in the data or in how the\nquery was built — rather than a real business result. None of these prove a\nbug; they're signals to double-check and, if they hold up, to mention rather\nthan silently pass through.\n\n- **Negative values on a measure that's never legitimately negative** — spend,\n  installs, revenue, clicks, impressions. Check `list_metrics`'s description\n  first in case the view has a genuine clawback/adjustment measure where a\n  negative value is expected.\n- **A zero-count gap for a day sandwiched between non-zero days** in an\n  otherwise continuous range — different from zero at the very start of a\n  range (before the app existed) or the very end (today, still incomplete),\n  which are expected.\n- **A day-over-day swing of an order of magnitude or more** with no filter\n  change to explain it (e.g. spend jumps from $500/day to $50,000/day\n  overnight) — plausible as a real campaign launch, implausible as a steady\n  baseline shift.\n- **Implausible derived ratios** — CTR over 100%, ROAS wildly disproportionate\n  to spend, CPI negative or near-zero while spend is nonzero, or (in\n  `eac_cohorts_view`) `proceeds` exceeding `revenue` — proceeds is revenue net\n  of store fees, so it should never be the larger number.\n- **Totals that don't reconcile across two views describing the same thing**\n  under matching filters (e.g. \"EAC installs\" via `events_view` vs. via\n  `eac_cohorts_view` with `media_source != 'apple'` giving very different\n  numbers for the same app/date range) — don't silently pick one number over\n  the other.\n- **Any row dated after today** showing nonzero installs/spend/revenue — this\n  has no legitimate explanation (unlike the others above, there's no\n  \"plausible\" version of this one) and usually points to a timezone or\n  date-arithmetic bug either upstream or in the query itself.\n\n**One exception, to avoid a false alarm:** `total_spend`, `impressions`, and\n`clicks` are synced from each ad network's own API, and the tooltip on those\nmeasures already warns same-day values are unreliable — don't treat *today's*\nfigures for those three specifically as a zero-count gap or a swing. This\ndoesn't extend to installs or revenue, which aren't network-synced, so a\nsame-day drop in those is still worth flagging.\n\n**A separate, higher-urgency case: a scope leak.** If any returned row's\n`app_id`/`app_name` isn't one of the app_ids `whoami` reported for this\ncredential, stop — that's not a data-quality quirk, it's a potential\nauthorization bug. Tell the user immediately rather than\nfolding it into a routine \"heads up,\" and always call `report_data_anomaly`\nfor it, regardless of whether the user seems to care about the specific\nnumbers.\n\nWhen one of the other cases holds, say so in plain language alongside the\nanswer — e.g. \"Heads up: spend on March 4th shows as -$120, which shouldn't\nhappen for an ad-spend metric — the rest of the numbers below look normal.\"\nDon't withhold the data or refuse to answer; surface the anomaly and let the\nuser judge how much to trust the number.\n\nAfter warning the user, also call `report_data_anomaly` with the query that\nproduced it (measures/dimensions/filters/time_dimension/date_range) and the\nspecific anomalous row(s) — this is in addition to the user-facing warning,\nnot instead of it, and turns the moment into an inbound bug report the\nAppstack team can triage. Don't call it for something the user themselves\nflagged as expected/explained (e.g. \"yeah we know, campaign launched that\nday\") — only for a shape you independently judged to look wrong.\n\n## Tool inventory\n\n| Tool | Use it for | Notes |\n| --- | --- | --- |\n| `whoami` | App/org scope, and each app's `project_id`. Call first, every session. | |\n| `find_organization` | Resolving a company name to app scope. | Platform-admin scope only |\n| `list_metrics` | Discovering views, then a view's measure/dimension names before querying. | Index first, then `view=\"...\"`. Call before guessing, not after failing |\n| `list_dimension_values` | Discovering valid filter values before filtering. | Same — call before guessing |\n| `query_metrics` | Actual numbers: spend, revenue, installs, ROAS, breakdowns. | |\n| `report_data_anomaly` | Logging a data inconsistency (see sanity-check rules above) for the Appstack team to review. | **Writes real data** — call after warning the user, not instead of it |\n| `list_integrations` | Whether an ad network/MMP/subscription platform is connected and its status. | |\n| `get_onboarding_checklist` | Setup progress for a project. | Pass `app_id` (from `whoami`), not an organization id. Several projects in scope → it lists them and asks |\n| `list_dashboards` / `get_dashboard` | Saved dashboards and their widgets' configured measures/dimensions — already resolved to full `cube_name.field_name` paths, ready for `query_metrics`. | `list_dashboards` takes an `app_id` (from `whoami`) or a `project_id` (also on each `whoami` app) — never an organization id |\n| `list_user_journeys` / `get_user_journey_filters` / `get_user_journey` | Per-user event timelines (\"Appstack ID history\"). | Call the `_filters` variant before filtering, same discover-first rule |\n| `create_link` / `list_links` / `get_link` / `update_link` / `delete_link` | Standard links only (not ad-network links). Each link carries `custom_params` (its client-defined query params, read-only). | **These write real data** — confirm what you're about to create/change/delete before calling, don't chain them speculatively |\n\n## Efficiency patterns\n\n- **Reuse context, don't re-fetch.** If `list_metrics`'s schema or\n  `whoami`'s scope is already in the conversation, use it — don't call the\n  same discovery tool again \"to be safe\" within the same session.\n- **Batch what you can reason about together.** If a request needs spend by\n  media source *and* installs by country, that's two `query_metrics` calls\n  with different dimensions/measures, not four separate single-purpose ones.\n- **Prefer `get_dashboard` over rebuilding a query from scratch** when the\n  user references an existing dashboard/report by name — its widgets already\n  encode the right measures, dimensions, and filters.\n- **`include_total` on `query_metrics` costs an extra backend query** — only\n  set it when the row count itself matters, not by default.\n\n## Common mistakes to avoid\n\n- Guessing a measure name because it \"sounds right\" (see `install` vs.\n  `installs` above) instead of calling `list_metrics`.\n- Scanning `whoami`'s app list by hand to find a company name instead of\n  calling `find_organization`.\n- Re-calling `whoami`/`list_metrics` repeatedly in one session when the\n  answer hasn't changed.\n- Calling `create_link`/`update_link`/`delete_link` without confirming the\n  specifics with the user first — these mutate real data, unlike every other\n  tool.\n- Treating an empty `query_metrics` result as \"no data exists\" without first\n  checking whether the filter value was ever confirmed via\n  `list_dimension_values` — an unrecognized value returns empty, not an error.\n"
}

SHA-256 of public snapshot: 9e9dbb3d219deedff470c09228971539aa953f6c68d6f789c61dc5065e62b1b8