Semaphor
MCP

What a Number Means

Compare a measure with an earlier period or a target, say which way is good, choose how the change shows, and run a table calculation such as a running total or a share of the total, through Semaphor MCP.

An agent can set what each number on a card means, in the same words the editor uses: what a measure is compared with, which direction is good, how the change shows, and a table calculation such as a running total or a share of the total. It reads each one in the outline and changes it through semaphor_propose_change and semaphor_apply_change like every other change (see Making Changes).

Compare with last year, shown as a percent change
{
  "dashboardId": "d_8f2k",
  "changes": [
    {
      "op": "card.update",
      "cardId": "c_purchases_trend",
      "query": { "measures": [{ "name": "net_purchase_value", "comparison": { "kind": "previous_year" } }] },
      "comparisonDisplay": { "mode": "percent_change" }
    }
  ]
}

The proposal reads it back in plain words:

Compares Net Purchase Value in "Net purchase value by month" with the same period last year, and shows the comparison as a percent change.

Read What a Card Computes

semaphor_get_dashboard_details with fields: ["outline"] shows each measure's comparison and table calculation inside the card's query, and the card's comparison settings beside its columns:

A card's comparisons and calculations in the outline
{
  "cardId": "c_purchases_trend",
  "title": "Net purchase value by month",
  "type": "line",
  "query": {
    "measures": [
      { "name": "net_purchase_value", "comparison": { "kind": "previous_year" } },
      { "name": "net_tons", "transform": { "kind": "moving_average", "orderBy": { "field": "purchase_date", "direction": "asc" }, "resetBy": [], "window": 3, "minPeriods": 1 } }
    ],
    "dateField": "purchase_date",
    "timeGrain": "month"
  },
  "comparisonDisplay": { "mode": "percent_change", "precision": 1 },
  "columns": {
    "Net Purchase Value": { "field": "net_purchase_value", "role": "measure", "polarity": "lower_is_better", "inherits": ["label", "format"] }
  }
}

A measure with no comparison follows the dashboard's date comparison. A table calculation shows every field it runs with, defaults included. Send what you read back and nothing changes, so an agent can copy a card's query, change one measure, and send it back.

Comparisons and Targets

A measure in query.measures takes a comparison:

kindWhat the measure is compared with
inheritThe dashboard's date comparison, when a viewer picks one. This is the default.
offNothing, even when the dashboard compares
previous_periodThe period before. offsetPeriods goes further back; alignment: "weekday" matches weekdays; toDate: true compares the elapsed part of an incomplete period
previous_yearThe same period last year, with offsetYears, alignment and toDate
targetA fixed number, targetValue
A target of 1,000,000 on a KPI
{ "op": "card.update", "cardId": "c_total", "query": { "measures": [{ "name": "net_purchase_value", "comparison": { "kind": "target", "targetValue": 1000000 } }] } }
  • Comparisons show on KPIs, tables and bar, line and area charts; targets on KPIs and tables.
  • A period or year comparison needs a date to compare over: the card's date grouping (on a KPI, its Trend By: dateField with timeGrain), or the dataset's primary date.
  • It shows while a date range applies to that date: the card's own timeWindow, or a dashboard date filter that reaches the card. A proposal says so in its warnings when none does yet.
  • A measure keeps its comparison when the change doesn't state one, even when the rest of the query changes.

Which Way Is Good

A measure's polarity in columns says whether a rise is good (higher_is_better, the default), bad (lower_is_better) or neither (neutral). It colors the change on that card.

A drop in returns shows as good
{ "op": "card.update", "cardId": "c_returns", "columns": { "Returns": { "polarity": "lower_is_better" } } }

When the semantic model sets a measure's direction, every card follows it: the outline lists polarity in the column's inherits, and a card built through the MCP takes it too.

How the Change Shows

SettingValues
comparisonDisplay{ "show": false } hides the comparison; mode is difference, percent_change, both or ratio; precision sets the decimals of a percent or ratio, 0 to 6, and null returns it to the default
inheritComparisonfalse: the card's measures without a comparison of their own ignore the dashboard's date comparison. true follows it again.

A KPI, its tab header and its tile in a document show the change the same way.

Table Calculations

A measure in query.measures takes a transform, calculated over the rows the card returns:

kindWhat the measure showsIts own fields
running_totalThe total so far
moving_averageThe average of the last window pointswindow (7), minPeriods (1)
difference_from_previousThe change from the previous rowmode: difference or percent_change
percent_of_totalEach row's share of the totalscope: grand_total or partition_total; output: percent or fraction
  • orderBy names the grouping it runs along, with an optional direction. Left out, it runs along the card's date grouping; a card without one names the grouping to run along.
  • resetBy lists the groupings it starts over for. Left out, it starts over for every other grouping.
  • A share of the total shows as a percent unless the column has a format of its own.
  • transform: null removes it. A measure's own comparison and a table calculation replace each other, and the sentence says so.
A running total over the months, starting over for each facility
{
  "op": "card.update",
  "cardId": "c_monthly_by_facility",
  "query": {
    "measures": [{ "name": "net_purchase_value", "transform": { "kind": "running_total", "orderBy": { "field": "purchase_date" }, "resetBy": ["facility_id"] } }]
  }
}

Table calculations run on tables and bar, line, area and pie charts.

In semaphor_analyze

semaphor_analyze takes the same comparison and transform on its measures. A comparison applies to the whole analysis, so state it once (top level or on the measures), with kind and targetValue. A table calculation runs on the result rows:

Each facility's share of net tons
{
  "domainId": "dom_ops",
  "datasetName": "purchase_line",
  "measures": [{ "name": "net_tons", "transform": { "kind": "percent_of_total" } }],
  "dimensions": ["facility_id"]
}

A KPI's Trend By

A KPI's Trend By is its query's dateField and timeGrain. It is the date a comparison runs over, and it sets where a to-date comparison cuts the current period.

Trend a KPI by purchase date, monthly
{ "op": "card.update", "cardId": "c_total", "query": { "dateField": "purchase_date", "timeGrain": "month" } }

What Needs a Yes

A comparison, a target, a table calculation and inheritComparison change what a card computes, so their proposal returns confirm: "needed". A polarity or a comparisonDisplay only changes how a card reads, so on its own it returns confirm: "not_needed".

On this page