diff --git a/modules/ROOT/pages/common/nav-embedding.adoc b/modules/ROOT/pages/common/nav-embedding.adoc index 224e2beb0..ecd1fa63b 100644 --- a/modules/ROOT/pages/common/nav-embedding.adoc +++ b/modules/ROOT/pages/common/nav-embedding.adoc @@ -98,6 +98,7 @@ Customize and integrate ** link:{{navprefix}}/runtime-overrides[Runtime overrides] ** link:{{navprefix}}/runtime-filters[Runtime filters] ** link:{{navprefix}}/runtime-params[Runtime Parameters] +** link:{{navprefix}}/liveboard-filter-and-parameter-overrides[Liveboard filter and parameter overrides] * link:{{navprefix}}/action-config[Customize menus] ** link:{{navprefix}}/actions[Action IDs in the SDK] * link:{{navprefix}}/events-app-integration[Events and app interactions] @@ -167,9 +168,8 @@ Customize and integrate Reference guides and changelog * link:{{navprefix}}/VisualEmbedSdk[Visual Embed SDK Reference] -* link:{{navprefix}}/embed-sdk-changelog[Changelog] -** link:{{navprefix}}/embed-sdk-changelog[Visual Embed SDK] -** link:{{navprefix}}/mobile-sdk-changelog[Mobile Embed SDK] +* link:{{navprefix}}/embed-sdk-changelog[Visual Embed SDK Changelog] +* link:{{navprefix}}/mobile-sdk-changelog[Mobile Embed SDK] [.sidebar-title] Embedding tutorials diff --git a/modules/ROOT/pages/common/nav-in-product-help.adoc b/modules/ROOT/pages/common/nav-in-product-help.adoc index 8509e5f2b..74a33cca9 100644 --- a/modules/ROOT/pages/common/nav-in-product-help.adoc +++ b/modules/ROOT/pages/common/nav-in-product-help.adoc @@ -114,6 +114,7 @@ Customize and integrate ** link:{{navprefix}}/runtime-overrides[Runtime overrides] ** link:{{navprefix}}/runtime-filters[Runtime filters] ** link:{{navprefix}}/runtime-params[Runtime Parameters] +** link:{{navprefix}}/liveboard-filter-and-parameter-overrides[Liveboard filter and parameter overrides] * link:{{navprefix}}/action-config[Customize menus] ** link:{{navprefix}}/actions[Action IDs in the SDK] * link:{{navprefix}}/events-app-integration[Events and app interactions] diff --git a/modules/ROOT/pages/customize-liveboard-filters.adoc b/modules/ROOT/pages/customize-liveboard-filters.adoc index c695e2606..480e03561 100644 --- a/modules/ROOT/pages/customize-liveboard-filters.adoc +++ b/modules/ROOT/pages/customize-liveboard-filters.adoc @@ -52,7 +52,9 @@ The `showMaskedFilterChip` property is also available in full application embedd [#filters-at-load] == Apply filters at load time -By default, Liveboard filters cannot be applied at load. You can either embed a Liveboard that already includes filters or use the xref:runtime-filters.adoc[runtime filters] feature in the Visual Embed SDK to apply filters at load time. +To open an embedded Liveboard with specific filter and parameter values, set `liveboardOverride`. The values appear in the Liveboard's filter and parameter chips. For more information, see xref:liveboard-overrides.adoc[Liveboard filter and parameter overrides]. To change values after load, use `HostEvent.UpdateFilters`. + +To add a filter condition that doesn't appear in the chips, use xref:runtime-filters.adoc[runtime filters] instead. [IMPORTANT] ==== @@ -116,6 +118,8 @@ To listen for filter and parameter changes, use `EmbedEvent.FilterChanged` and ` For more information, see xref:events-hostEvents.adoc#liveboard-group-events[Liveboard group and parameter events] and xref:events-hostEvents.adoc#applicability-host-events[Scoped filter and parameter host events]. +To set tab-level and group-level filter and parameter values at load, use the `liveboardOverride` property. For more information, see xref:liveboard-overrides.adoc#scoping[Target a model, tab, or group] in xref:liveboard-overrides.adoc[Liveboard filter and parameter overrides]. + [#update-filters] == Updating filters Use the following host events in the Visual Embed SDK to update filters: diff --git a/modules/ROOT/pages/embed-pinboard.adoc b/modules/ROOT/pages/embed-pinboard.adoc index 2664f383a..17359dd9e 100644 --- a/modules/ROOT/pages/embed-pinboard.adoc +++ b/modules/ROOT/pages/embed-pinboard.adoc @@ -115,6 +115,8 @@ The customization options are grouped into the following pages: |Header visibility and compact header, tabs, visible tabs and visualizations, tile gutter, Note tiles, responsive breakpoints and 12-column layout, and grouping and styling. |xref:customize-liveboard-filters.adoc[Customize Liveboard filters] |Filter chip visibility, masked filter chips, filters at load time, contextual filtering at the Liveboard, tab, and group levels, and updating filters with host events. +|xref:liveboard-overrides.adoc[Set filter and parameter values at load] +|Values of existing filter and parameter chips at load, through `liveboardOverride` or the Liveboard URL, with targeting by model, tab, or group. |xref:liveboard-data-downloads.adoc[Customize data refresh and download options] |Browser data cache and refresh, PDF, XLSX, and CSV downloads, download action IDs and host events, and scheduled exports. |==== diff --git a/modules/ROOT/pages/filters_overview.adoc b/modules/ROOT/pages/filters_overview.adoc index 3a2f51c72..21dd4c200 100644 --- a/modules/ROOT/pages/filters_overview.adoc +++ b/modules/ROOT/pages/filters_overview.adoc @@ -37,7 +37,8 @@ xref:runtime-filters.adoc#_maximum_filter_count[Runtime filter limit] for more i 4. link:https://docs.thoughtspot.com/cloud/latest/liveboard-filters[Liveboard filters, window=_blank] + Liveboard filters are visible as UI components at the top of a Liveboard page. By default, a Liveboard filter applies to all visualizations on the Liveboard. Starting with ThoughtSpot Cloud 26.10.0.cl, a filter can also be scoped to a specific tab or group. For more information, see <>. When a filter is clicked, a modal with filter options appropriate for the data type is displayed. + -Liveboard users can add or modify filters as needed. If you are embedding a Liveboard that includes preset filters, you can programmatically update, reset, or remove filters using `HostEvent.UpdateFilters`. +Liveboard users can add or modify filters as needed. If you are embedding a Liveboard that includes preset filters, you can programmatically update, reset, or remove filters using `HostEvent.UpdateFilters`. + +To set Liveboard filter and parameter values at load, use xref:liveboard-overrides.adoc[Liveboard filter and parameter overrides]. Overrides set the values of the Liveboard filters in this layer. They are not runtime filters (layer 3). 5. link:https://docs.thoughtspot.com/cloud/latest/liveboard-filters-cross[Liveboard cross filters, window=_blank] + Cross filters are ad-hoc filters based on user selection. These filters are used for brushing and linking Liveboard visualizations. + @@ -182,7 +183,7 @@ Any data restrictions resulting from filter rules will be seen in the visible fi xref:runtime-filters.adoc[Runtime filters] can be applied at load via Visual Embed SDK, REST API, or URL parameters. -Liveboard filters cannot be applied at load. However, they can be updated using `HostEvent.UpdateFilters` in the SDK. +To set Liveboard filter values at load, use xref:liveboard-overrides.adoc[Liveboard filter and parameter overrides]. After load, Liveboard filters can be updated using `HostEvent.UpdateFilters` in the SDK. Search query filters can be applied at load by specifying them in the initial search query when embedding an Answer or Spotter session. For example, in Spotter embed, you can use the `searchQuery` property to set a pre-defined search (including filters) at load. @@ -196,7 +197,7 @@ If the column already has a Liveboard filter and the user applies cross filters, Whenever any user action affects a cross filter, a link:https://developers.thoughtspot.com/docs/Enumeration_EmbedEvent#_crossfilterchanged[EmbedEvent.CrossFilterChanged] fires, which can then be used to trigger a specific action. == Updating filters using host events in Visual Embed SDK -There is no programmatic way to adjust the filter values before loading the Liveboard, but there are events that can adjust the values after the Liveboard is rendered. +To set Liveboard filter values at load, use xref:liveboard-overrides.adoc[Liveboard filter and parameter overrides]. To adjust the values after the Liveboard is rendered, use the following events. === OpenFilter event If you have hidden the Liveboard header, you can trigger the action of opening a filter modal dialog using link:https://developers.thoughtspot.com/docs/Enumeration_HostEvent#_openfilter[HostEvent.OpenFilter]: @@ -207,6 +208,7 @@ liveboardEmbed.trigger(HostEvent.OpenFilter, { columnId: ''}) ---- +[#_updatefilters_event] === UpdateFilters event The link:https://developers.thoughtspot.com/docs/Enumeration_HostEvent#_updatefilters[HostEvent.UpdateFilters] directly updates the values of an existing filter currently applied on a Liveboard: diff --git a/modules/ROOT/pages/liveboard-overrides.adoc b/modules/ROOT/pages/liveboard-overrides.adoc new file mode 100644 index 000000000..5e3b56ccb --- /dev/null +++ b/modules/ROOT/pages/liveboard-overrides.adoc @@ -0,0 +1,768 @@ += Liveboard filter and parameter overrides +:toc: true +:toclevels: 2 + +:page-title: Liveboard filter and parameter overrides +:page-pageid: liveboard-filter-and-parameter-overrides +:page-description: Set the values of a Liveboard's existing filter and parameter chips at load time, through the Liveboard URL or the liveboardOverride property of the Visual Embed SDK. + +[earlyAccess eaBackground]#Early Access# + +Liveboard filter and parameter overrides set the values of a Liveboard's existing filter and parameter chips at load time. + +== Overview + +You can supply overrides in two ways: + +* In the Liveboard URL, through the `filter` and `parameter` query parameters. +* In an embedded Liveboard, through the `liveboardOverride` property of the Visual Embed SDK. + +Use overrides for these tasks: + +* Open an embedded Liveboard with filters and parameters that match your application's context, such as the signed-in customer's region or the period selected in your app. +* Build links that open a Liveboard in a specific filtered state, for example, a link from a row in one Liveboard to a detail Liveboard filtered to that row's values. +* Share a filtered state of a Liveboard without saving a personalized view for every combination of values. + +[#comparison] +== How overrides differ from runtime filters and runtime parameters + +xref:runtime-filters.adoc[Runtime filters] work on model columns. They add a filter condition behind the scenes and combine with the Liveboard's existing filters using an AND condition. They do not appear in the filter chips. + +Overrides work on the Liveboard's filter and parameter chips instead. They replace the values selected in a chip, and the chip shows the new values. + +[width="100%", cols="4,3,3,3,3"] +[options='header'] +|===== +| +|Liveboard overrides +|Runtime filters +|Runtime parameter overrides +|Personalized views + +|*What it sets* +|Values in existing filter and parameter chips +|A filter condition on a model column +|A parameter's value +|A saved set of chip values + +|*Shown in the chips* +|Yes +|No +|No (chip is hidden) +|Yes + +|*Needs a chip* +|Yes +|No +|No +|Yes + +|*Effect on existing values* +|Replaces the selection +|Adds an AND condition +|Replaces the value +|Replaces the selection + +|*Rolling dates* +|Yes +|No +|Not applicable +|Yes + +|*Targets a model, tab, or group* +|Yes +|No +|No +|Not applicable + +|*Works on* +|Liveboards +|Liveboards, Answers, visualizations, REST APIs +|Liveboards, Answers, REST APIs +|Liveboards + +|*Saved* +|No +|No +|No +|Yes +|===== + +To change values after the Liveboard loads, use host events. `UpdateFilters` and `UpdateParameters` update the chips and fetch data again. `UpdateRuntimeFilters` replaces the runtime filter for any column you specify with the new value. Filters on columns you don't specify are preserved. + +Use overrides when users should see and adjust the values in the Liveboard's own chips. + +== Before you begin + +* Your administrator must turn on *Filtered Liveboard URLs* on the *Early Access* page of the Admin portal. The setting is Org-aware. When it is off, overrides are ignored without an error. +* To use `liveboardOverride`, use Visual Embed SDK 1.54.0 or later. +* Every filter or parameter you want to set must already exist as a chip on the Liveboard. Overrides never add a chip. +* To target a tab or group, xref:customize-liveboard-filters.adoc#contextual-liveboard-filtering[contextual filtering] (tab-level and group-level filters) must be turned on. When it is off, any override that names a tab or group is ignored. + +== Example Liveboard + +Every example on this page uses a Liveboard named *Retail Sales Overview*, set up as follows: + +* Two models: *Retail Sales* and *Returns*. Both have *Region* and *Store Name* columns. +* Two tabs: *Summary* and *Regional Detail*. Each tab has a group named *Top Stores*. +* The *Top Stores* group on the *Regional Detail* tab has visualizations from both models. + +The Liveboard has the following filter chips. The examples on this page refer to each chip by its number. + +[width="100%", cols="1,3,2,3,6"] +[options='header'] +|===== +|# |Chip |Type |Model |Where it is +|1 |Region |Attribute |Retail Sales |Liveboard +|2 |Region |Attribute |Returns |Liveboard +|3 |Order Date |Date |Retail Sales |Liveboard +|4 |Sales |Measure |Retail Sales |Liveboard +|5 |Product Type |Attribute |Retail Sales |Summary tab +|6 |Product Type |Attribute |Retail Sales |Regional Detail tab +|7 |Store Name |Attribute |Retail Sales |Top Stores group on the Summary tab +|8 |Store Name |Attribute |Retail Sales |Top Stores group on the Regional Detail tab +|9 |Store Name |Attribute |Returns |Top Stores group on the Regional Detail tab +|===== + + +The Liveboard also has the following parameter chips, all at the Liveboard level and from the Retail Sales model: + +[width="100%", cols="3,2,5"] +[options='header'] +|===== +|Parameter |Type |Allowed values +|Discount Rate |DOUBLE |0 to 0.5 +|Top N |INT |1 to 50 +|Target Region |VARCHAR |West, East, Central, South +|Forecast Start |DATE |Any date +|===== + +[#embed] +== Apply overrides in an embedded Liveboard + +Set `liveboardOverride` in the `LiveboardEmbed` configuration. The overrides apply when the Liveboard loads. + +[NOTE] +==== +In the 26.11 release, this feature is supported only in `LiveboardEmbed`. +==== + +=== Step 1: Set one filter + +[source,JavaScript] +---- +const embed = new LiveboardEmbed('#embed-container', { + frameParams: { width: '100%', height: '100%' }, + liveboardId: '', + liveboardOverride: { + dataSourceOverride: [ + { + filterQuery: [ + { filterQueryToken: 'Order Date=last 30 days' }, // Chip 3 + ], + }, + ], + }, +}); +embed.render(); +---- + +Result: chip 3 shows the last 30 days. All other chips keep their saved values. + +=== Step 2: Set several filters and parameters + +Put each override in its own token: filter overrides in `filterQuery`, and parameter overrides in `parameterQuery`. + +[source,JavaScript] +---- +liveboardOverride: { + dataSourceOverride: [ + { + filterQuery: [ + { filterQueryToken: 'Order Date=this quarter' }, // Chip 3 + { filterQueryToken: 'Sales>10000' }, // Chip 4 + ], + parameterQuery: [ + { parameterQueryToken: 'Discount Rate=0.15' }, // Discount Rate chip + { parameterQueryToken: 'Top N=10' }, // Top N chip + ], + }, + ], +}, +---- + +Result: chip 3 shows this quarter, chip 4 shows Sales greater than 10000, Discount Rate is 0.15, and Top N is 10. All other chips keep their saved values. + +To clear a filter chip, leave the value empty: + +[source,JavaScript] +---- +filterQuery: [ + { filterQueryToken: 'Sales=' }, // Chip 4 +], +---- + +Result: chip 4 is cleared and shows all values. + +=== Step 3: Use the same name in several places + +An override without `dataSourceIdentifier`, `tabName`, or `groupName` updates every chip with that name, on every model and at every level. + +[source,JavaScript] +---- +filterQuery: [ + { filterQueryToken: 'Region=West' }, // Chips 1, 2 + { filterQueryToken: 'Product Type=Shoes' }, // Chips 5, 6 + { filterQueryToken: 'Store Name=Downtown' }, // Chips 7, 8, 9 +], +---- + +Result: + +* Both Region chips show West (Region is on both models). +* Both Product Type chips show Shoes (Product Type is on both tabs). +* All three Store Name chips show Downtown (Store Name is in both groups and on both models). + +=== Step 4: Target a model, tab, or group + +Use these properties to narrow which chips an override updates: + +* `dataSourceIdentifier`: set on the entry, so it applies to every override in the entry. Use a separate entry for each model. +* `tabName`: set on each override. A tab also covers the groups on it, but not Liveboard-level chips. +* `groupName`: set on each override. A group name matches every group with that name. Add `tabName` to pick the group on one tab. + +In this table, the numbers refer to the chips of the example Liveboard. An empty cell means the property is not set. + +[width="100%", cols="21,18,14,29,18"] +[options='header'] +|===== +|dataSourceIdentifier |tabName |groupName |filterQueryToken |Chips updated +| | | |`Store Name=Downtown` |7, 8, 9 +|Retail Sales | | |`Region=West` |1 +|Retail Sales | | |`Store Name=Downtown` |7, 8 +| |Regional Detail | |`Product Type=Shoes` |6 +| |Regional Detail | |`Store Name=Downtown` |8, 9. The tab covers its group. +| | |Top Stores |`Store Name=Downtown` |7, 8, 9. Both groups are named Top Stores. +| |Regional Detail |Top Stores |`Store Name=Downtown` |8, 9 +|Retail Sales | |Top Stores |`Store Name=Downtown` |7, 8 +|Returns |Regional Detail | |`Store Name=Downtown` |9 +|Retail Sales |Regional Detail |Top Stores |`Store Name=Downtown` |8 +|===== + +For example, the last row in code: + +[source,JavaScript] +---- +{ + dataSourceIdentifier: 'Retail Sales', + filterQuery: [ + { + tabName: 'Regional Detail', + groupName: 'Top Stores', + filterQueryToken: 'Store Name=Downtown', // Chip 8 + }, + ], +}, +---- + +Result: only chip 8 shows Downtown. Chips 7 and 9 keep their saved values. + +==== Overrides that are skipped + +Each of these overrides is skipped: + +[width="100%", cols="21,13,14,29,23"] +[options='header'] +|===== +|dataSourceIdentifier |tabName |groupName |filterQueryToken |Why the override is skipped +| |Summary | |`Region=West` |A tab doesn't cover Liveboard-level chips, and the Summary tab has no Region chip. +| |Summary |Top Stores |`Product Type=Shoes` |A group doesn't cover its tab's chips. Product Type is on the Summary tab, not in the group. +|Returns |Summary | |`Store Name=Downtown` |The Summary tab has no Store Name chip from Returns. +|Retail | | |`Region=West` |No model is named Retail. When the model doesn't match, every override in the entry is skipped. +|===== + +Only the skipped override is affected. Its chips keep their saved values, and the other overrides still apply. + +=== Step 5: Put it all together + +[source,JavaScript] +---- +const embed = new LiveboardEmbed('#embed-container', { + frameParams: { width: '100%', height: '100%' }, + liveboardId: '', + liveboardOverride: { + dataSourceOverride: [ + { + // No dataSourceIdentifier: matching chips on every model + filterQuery: [ + { filterQueryToken: 'Order Date=last 30 days' }, // Chip 3 + { tabName: 'Regional Detail', filterQueryToken: 'Product Type=Shoes' }, // Chip 6 + ], + parameterQuery: [ + { parameterQueryToken: 'Discount Rate=0.15' }, // Discount Rate chip + ], + }, + { + dataSourceIdentifier: 'Retail Sales', + filterQuery: [ + { filterQueryToken: 'Region=West, East' }, // Chip 1 + ], + }, + { + dataSourceIdentifier: 'Returns', + filterQuery: [ + { tabName: 'Regional Detail', filterQueryToken: 'Store Name=Downtown' }, // Chip 9 + ], + }, + ], + }, +}); +embed.render(); +---- + +Result: chips 1, 3, 6, and 9, and the Discount Rate chip, are updated. Chips 2, 4, 5, 7, and 8, and the other parameter chips, keep their saved values. No two overrides target the same chip. + +=== Liveboard override SDK flag properties + +[width="100%", cols="2,5a"] +|==== +|Property |Description + +|`dataSourceOverride` __Required__ +|__Array__. A list of override entries. Each entry groups the filter and parameter overrides for one model, or for all models when `dataSourceIdentifier` is omitted. Each entry has the `dataSourceIdentifier`, `filterQuery`, and `parameterQuery` properties. + +|`dataSourceIdentifier` __Optional__ +|__String__. The name or GUID of a model used by the Liveboard. Names are case-insensitive. + +* When set, the entry's overrides update only that model's chips. +* When omitted or empty, they update matching chips on every model. +* If the identifier does not match a model on the Liveboard, every override in the entry is ignored. + +|`filterQuery` __Optional__ +|__Array__. Filter overrides. Each item has the following properties: + +* `filterQueryToken`: __String__. Required. One filter override, such as `Region=West`. See <>. +* `tabName`: __String__. Optional. The tab whose chips to update. +* `groupName`: __String__. Optional. The group whose chips to update. + +|`parameterQuery` __Optional__ +|__Array__. Parameter overrides. Each item has the following properties: + +* `parameterQueryToken`: __String__. Required. One parameter override, such as `Top N=10`. See <>. +* `tabName`: __String__. Optional. The tab whose chips to update. +* `groupName`: __String__. Optional. The group whose chips to update. +|==== + +Each token holds exactly one override. Do not join overrides with `;` or add `Model::Tab::` prefixes inside a token. Use the `dataSourceIdentifier`, `tabName`, and `groupName` properties instead. + +Leave out properties you do not need rather than setting them to empty strings. + +=== Behavior in an embedded Liveboard + +* `liveboardOverride` is the only way to pass overrides to an embedded Liveboard. Unlike runtime filters, which you can add to an embedded object's URL, adding `filter=` or `parameter=` to the URL of an embedded Liveboard has no effect. +* Overrides apply when the Liveboard loads. Changing `liveboardOverride` after the Liveboard has rendered doesn't reload it. To change filter or parameter values after load, use the xref:filters_overview.adoc#_updatefilters_event[`UpdateFilters`] and `UpdateParameters` host events. +* Overrides with no token are dropped, and so are properties the SDK doesn't recognize. +* In an embedded Liveboard, overrides are not applied to a personalized view that the user loads, for example by selecting it from the Views menu. The view opens with its saved values. + +[#url] +== Apply overrides in a Liveboard URL + +Add the `filter` and `parameter` query parameters after the Liveboard's route, that is, after the `+#+`: + +---- +https://{ThoughtSpot-Host}/#/pinboard/{liveboard-id}?filter={filter overrides}¶meter={parameter overrides} +---- + +* Both keys are optional, and both are singular: `filter`, not `filters`. +* Put them in the query string after the route. If they come before the `+#+`, they are ignored. Legacy runtime filters work the other way: `col1`, `op1` and `val1` go before the `+#+`. +* You can combine them with other Liveboard URL parameters, such as a tab (`+#/pinboard/{liveboard-id}/tab/{tab-id}?filter=...+`) or a personalized view (`+?view={view-id}&filter=...+`). +* Separate overrides with a semicolon (`;`). +* Spaces around operators are optional: `Region=West` and `Region = West` are the same override. +* You can wrap the whole value in one pair of straight double quotes, for example `+filter="Region=West"+`. Curly quotes are not removed, and quoting individual overrides is not supported. + +=== URL examples + +Add these to the end of the Liveboard's URL, for example `+https://{ThoughtSpot-Host}/#/pinboard/{liveboard-id}?filter=Region=West, East+`. + +[width="100%", cols="3,5"] +[options='header'] +|===== +|Goal |Add to the URL +|Show the West and East regions |`+?filter=Region=West, East+` +|Show the last 30 days |`+?filter=Order Date=last 30 days+` +|Set three filters and a parameter |`+?filter=Region=West; Order Date=this quarter; Sales>10000¶meter=Discount Rate=0.15+` +|Update only the Retail Sales model's Region chip |`+?filter=Retail Sales::Region=West+` +|Update the Product Type chip on the Regional Detail tab |`+?filter=Regional Detail::Product Type=Shoes, Jackets+` +|Update the Store Name chips in the Top Stores group on the Regional Detail tab |`+?filter=Regional Detail::Top Stores::Store Name=Downtown, Airport+` +|Clear both Product Type chips |`+?filter=Product Type=+` +|Set two parameters |`+?parameter=Forecast Start=2026-07-01; Top N=10+` +|Apply on top of a personalized view |`+?view={view-id}&filter=Region=West+` +|===== + +=== Encoding + +You can type or paste these values as shown. The browser encodes spaces and other characters when it opens the URL. Encode a value yourself only in these cases: + +* The value contains `&`, `+++`, `%`, `+#+` or `\`. Write them as `%26`, `%2B`, `%25`, `%23` and `%5C`. Otherwise the browser reads them as part of the URL itself. +* You build the URL in code, or send it where spaces break links, such as email or chat. Use `encodeURIComponent()` on each value. For example, `+?filter=Region=West, East+` becomes `+?filter=Region%3DWest%2C%20East+`. + +[#filter-syntax] +== Filter syntax + +A filter override has this form: + +---- +{Column name}{operator}{value}[, {value}...] +---- + +* *Column name*: the name of the filter. It is case-insensitive, but it must match the whole name. Partial and fuzzy matches are not supported. Multi-word names need no quotes. +* *Operators*: `=`, `!=`, `<`, `+<=+`, `>`, `>=`. Which operators you can use depends on the column type, as shown in the tables below. +* *Lists*: separate values with commas. Spaces inside a value are kept, so `Store Name=New York Downtown, San Francisco Bay` is a list of two values. +* *Keywords*: keywords such as `between`, `last` and `today` are case-insensitive. + +=== Attribute columns + +[width="100%", cols="4,5"] +[options='header'] +|===== +|Input |Result +|`Region=West` |Region is West +|`Region=West, East, Central` |Region is any of West, East, Central +|`Region!=South` |Region is not South +|`Region!=South, Central` |Region is none of South, Central +|`+Store Name=Smith\, Jones and Co+` |One value that contains a comma +|`Region=` |Clears the chip, so all values are shown +|===== + +Not supported on attribute columns:: + +* `<`, `+<=+`, `>`, `>=` (for example, `Region>West`) +* `between` +* Contains, begins with, or ends with matching +* An empty list item, such as `Region=West,` or `Region=West,,East` +* `{null}`. Filtering on null values is not supported. + +[NOTE] +==== +Values are passed as written. Type them as they appear in the data. +==== + +=== Measure columns + +[width="100%", cols="4,5"] +[options='header'] +|===== +|Input |Result +|`Sales>10000` |Greater than 10000. `>=`, `<` and `+<=+` work the same way. +|`Sales=between 1000 5000` |Between 1000 and 5000, inclusive +|`Sales=` |Clears the chip +|===== + +Measure value requirements:: + +* Comparison operators take exactly one value. +* `between` takes exactly two values, separated by a space, with the smaller value first. +* Decimal values (`1250.75`) and negative values (`-200`) are supported. +* Thousands separators (`10,000`), currency symbols (`$100`), and percent signs are not supported. + +Not supported:: + +* `Sales>100, 200` +* `Sales=between 1000, 5000` (comma between the values) +* `Sales=between 5000 1000` (larger value first) +* `Sales!=between 1000 5000` (measures have no "not between") + +=== Date columns: rolling dates + +[width="100%", cols="4,5"] +[options='header'] +|===== +|Input |Result +|`Order Date=today` |Today. `yesterday` and `tomorrow` also work. +|`Order Date=this quarter` |The current quarter +|`Order Date=last 30 days` |The last 30 days +|`Order Date=last quarter` |The last quarter (same as `last 1 quarter`) +|`Order Date=last 24 hours` |The last 24 hours. Periods can be as short as `second`, `minute` and `hour`. +|`Order Date=next 2 weeks` |The next 2 weeks +|`Order Date=next quarter` |The next quarter (same as `next 1 quarter`) +|`Order Date=year to date` |Year to date. `week`, `month`, and `quarter to date` also work. +|`Order Date>last 2 months` |After the last 2 months +|`+Order Date<=this month+` |On or before this month +|`Order Date`, and `>=`. +* Rolling dates do not support `!=`. +* "To date" periods support only `=`. + +=== Date columns: fixed dates + +[width="100%", cols="53,47"] +[options='header'] +|===== +|Input |Result +|`Order Date=2026-08-31` |On that day. `!=`, `<`, `+<=+`, `>`, and `>=` also work. +|`Order Date=between 2026-01-01 2026-06-30` |Between the two dates, inclusive +|`Order Date!=between 2026-01-01 2026-06-30` |Not between the two dates +|`Order Date=january 2026` |The whole month +|`Order Date=q1 2026` |The whole quarter (`q1` to `q4`) +|`Order Date=2026` |The whole year +|`Order Date=` |Clears the chip +|===== + +Fixed date requirements:: + +* Write dates in ISO format, `yyyy-MM-dd`. A date is read as the start of that day in the ThoughtSpot cluster's time zone. +* Write month names in full (`january`, not `jan`), followed by a four-digit year. +* Month, quarter, and year values support only `=`. +* In a `between` range, the earlier date comes first. +* Commas are not allowed in date filters. + +Not supported:: + +* `08/31/2026` or other non-ISO formats +* `jan 2026` or `january` without a year +* `Q1-2026`, or fiscal quarters written as `fiscal q1 2026` +* Date and time values, such as `2026-08-31 10:00` +* Free-form phrases, such as `start of week`, `3 days ago`, `now`, or `last 7 days inclusive` +* `day to date` +* `Order Date=after last 2 months`. Write before and after as operators instead: `Order Date>last 2 months`. + +[#parameter-syntax] +== Parameter syntax + +A parameter override has this form: + +---- +{Parameter name}={value} +---- + +* *Parameter name*: the parameter's name. It is case-insensitive, but it must match the whole name. Partial and fuzzy matches are not supported. Multi-word names need no quotes. +* *Value*: exactly one value, written for the parameter's data type. + +[width="100%", cols="2,5a"] +|==== +|Data type |Description + +|`INT` +|Whole numbers only. For example, `Top N=10`. + +|`DOUBLE` +|Decimal values are supported. For example, `Discount Rate=0.15`. + +|`VARCHAR` +|Text values. For a parameter with a list of values, use a value or its display name. For example, `Target Region=West`. + +|`BOOLEAN` +|`true` or `false`. For example, `Include Returns=true`. + +|`DATE` +|ISO format, `yyyy-MM-dd`. For example, `Forecast Start=2026-07-01`. +|==== + +The value must satisfy the parameter's own restrictions: + +* For a parameter with a list of values, use one of the values or one of their display names. Display names are case-insensitive. +* For a parameter with a range, the value must fall inside the range. +* A value that doesn't meet these restrictions is ignored, and the chip keeps its current value. + +Not supported:: + +* Any operator other than `=` +* More than one value, such as `Top N=5, 10` +* An empty value (`Top N=`). Parameter chips cannot be cleared. +* `{null}` +* Dynamic date values, such as `Forecast Start=today` or `last 7 days` +* Date-time and time parameters + +== Special characters in values + +Some characters have a special meaning in an override. To use one of them as part of a value, put a backslash (`\`) before it. + +[width="100%", cols="2,5,4"] +[options='header'] +|===== +|Character |Input |Result +|Comma (`,`) +|`+Store Name=Smith\, Jones and Co+` +|One value, `Smith, Jones and Co`. Without the backslash, the comma separates two values, `Smith` and `Jones and Co`. + +|Equals sign (`=`) +|`+Product Type=Size\=XL+` +|The value `Size=XL`. Without the backslash, the second `=` is read as an operator. + +|Angle brackets (`<`, `>`) +|`+Product Type=\+` +|The value ``. Without the backslashes, `<` and `>` are read as operators. + +|Semicolon (`;`) +|`+Store Name=Mall\; North Wing+` +|The value `Mall; North Wing`. Without the backslash, the semicolon starts a new override. Needed in URLs only. + +|Double colon (`::`) +|`+Store Name=Airport\::Gate 5+` +|The value `Airport::Gate 5`. Without the backslash, `::` is read as a model, tab, or group prefix. Needed in URLs only. +|===== + +In the SDK, you do not need to escape `;` or `::`. Each token already holds one override, and models, tabs, and groups are set through properties. + +A single colon needs no escaping. For example, `Region=sales: eu` is read as one value. + +[#scoping] +== Target a model, tab, or group + +When no model, tab, or group is given, an override updates every chip with a matching name across all models and all levels (Liveboard, tab, and group). For example, `Region=West` updates the Region chip on both Retail Sales and Returns. + +To narrow an override, prefix it with names separated by `::` in a URL, or set `dataSourceIdentifier`, `tabName`, and `groupName` in the SDK. Chip numbers refer to the <<_example_liveboard,example Liveboard>>: + +[width="100%", cols="4,3,4"] +[options='header'] +|===== +|URL input |SDK equivalent |Chips updated +|`Region=West` |`{ filterQueryToken: 'Region=West' }` |Chips 1 and 2 +|`Retail Sales::Region=West` |`dataSourceIdentifier: 'Retail Sales'` |Chip 1 +|`Regional Detail::Product Type=Shoes` |`tabName: 'Regional Detail'` |Chip 6. A tab prefix also covers the groups on that tab, but not Liveboard-level chips. +|`Top Stores::Store Name=Downtown` |`groupName: 'Top Stores'` |Chips 7, 8, and 9. A group name matches every group with that name. +|`Regional Detail::Top Stores::Store Name=Downtown` |`tabName` and `groupName` |Chips 8 and 9 +|`Retail Sales::Top Stores::Store Name=Downtown` |`dataSourceIdentifier` and `groupName` |Chips 7 and 8 +|`Returns::Regional Detail::Store Name=Downtown` |`dataSourceIdentifier` and `tabName` |Chip 9 +|`Retail Sales::Regional Detail::Top Stores::Store Name=Downtown` |All three properties |Chip 8 +|===== + +How prefixes are matched in a URL: + +* Names are matched against the display names of models, tabs, and groups. Matching is case-insensitive. +* A model can also be given by its GUID. Tabs and groups are matched by name only. +* Prefixes always go in the order model > tab > group, and you can leave any of them out: +** With one prefix, the name is looked up as a model, then a tab, then a group. For example, in `Regional Detail::Product Type=Shoes`, `Regional Detail` doesn't match a model, so it is read as a tab. +** With two prefixes, the first is looked up as a model, then a tab, and the second must come after it in that order. For example, in `Regional Detail::Top Stores::Store Name=Downtown`, `Regional Detail` is read as a tab, so `Top Stores` must be a group. +** With three prefixes, they are read as model, tab, and group. +* Because models are looked up first, a name shared by a model and a tab is read as the model. To target the tab in that case, use the `tabName` property in the SDK. +* An override with more than three prefixes is ignored. +* An override whose prefix doesn't match a model, tab, or group on the Liveboard is ignored. + +Parameters use the same prefixes. For example, `Retail Sales::Discount Rate=0.15` updates the Discount Rate chip for the Retail Sales model only. + +There is no prefix that targets only a Liveboard-level chip. An override without a tab or group prefix updates all chips with a matching column name, including tab-level and group-level chips. To target only a tab or group, add its prefix. + +== How overrides behave + +=== Only existing chips are updated + +* An override updates a chip only if the chip already exists on the Liveboard. If a column or parameter is used in a visualization but has no chip, the override is ignored, and no chip is added. +* xref:runtime-parameters.adoc[Runtime parameter overrides] behave differently: they apply to any parameter used in the Liveboard. +* To change a linked parameter, target its primary parameter. Overrides that target a linked secondary parameter are ignored. + +=== Invalid overrides are skipped + +* Each override is applied independently. If an override can't be applied, because of a misspelled name, an invalid value, an unknown prefix, or a missing chip, only that override is skipped. The other overrides still apply, and the Liveboard always loads. +* No error or warning is shown, so verify the chip values after the Liveboard loads to confirm which overrides were applied. +* Overrides can be applied differently for different users. For example, an override on a model the user can't access is skipped for that user. + +=== Personalized views + +For a URL that includes a view (`+?view={view-id}&filter=...+`): + +* The Liveboard opens the view, then applies the overrides on top of it. +* Chips you don't override keep the view's values. +* The view shows as modified, and the user can update it with the new values. + +For a URL with overrides but no view: + +* The Liveboard opens with its saved values plus the overrides, even if the user has set a default view. +* The *Save view* option becomes available, so the user can save the overridden state as a new view. + +=== How long overrides last + +Overrides are not saved to the Liveboard. In a Liveboard URL, they stay in the URL while the user works in view mode: + +[width="100%", cols="3,6"] +[options='header'] +|===== +|Action |Result +|Reload the page |The overrides are applied again. Any filter changes the user made after the initial load are discarded. +|Switch tabs |The overrides stay in effect. +|Open and close *Explore* on a visualization |The overrides stay in effect. +|Reset filters in the filter window |The overrides are removed from the URL, and the chips return to their saved values. +|Enter edit mode |The overrides are removed from the URL, and the Liveboard returns to its saved state. Any other filter changes made in view mode are discarded too. Saving the Liveboard never saves override values. +|Save or update a personalized view |The view stores the values currently shown, including overridden ones. +|Open the Liveboard without the `filter` and `parameter` keys |The Liveboard opens with its saved values, or with the default view. +|===== + +=== Downloads, schedules, and alerts + +* Downloads from the Liveboard, such as PDF or a visualization's CSV, use the overridden values. +* Scheduled Liveboard jobs and alerts use the saved values. Overrides are not applied to them. + +[#precedence] +=== Security and precedence + +* Overrides are not a security feature. Anyone who can edit the URL can change the values or remove them. To restrict data, use xref:rls-rules.adoc[row-level security (RLS)] or the xref:abac-user-parameters.adoc[ABAC and JWT security methods]. +* Overrides can't expose data the user isn't allowed to see. RLS applies to the overridden values as it does to any filter. Overrides on columns the user can't access are skipped. +* A parameter can get its value from several places. When more than one sets it, ThoughtSpot uses the value from the highest-priority source. For example, a JWT token pinning `Discount Rate` to `0.1` takes precedence over a URL setting it to `0.15`. Sources, from highest to lowest priority: +.. A value pinned through ABAC with a JWT token +.. A runtime parameter override (`runtimeParameters`, or `param1` and `paramVal1` in the URL) +.. A Liveboard parameter override +.. The personalized view's value +.. The Liveboard's saved value +.. The parameter's default value +* Avoid setting the same parameter with both a runtime parameter override and a Liveboard override. The runtime override wins, and the chip is hidden. +* Runtime filters and Liveboard filter overrides can be used together. The runtime filter restricts the column's data, the override sets the chip's selection, and both apply. + +== Limitations + +* Overrides work on Liveboards only. Answers, saved Answers, Search, and Spotter don't support them, in the UI or in embeds. +* In embedded deployments, only `LiveboardEmbed` is supported. Full application embedding (`AppEmbed`) is not supported in this release. +* Overrides apply only at load. To change values after load in an embed, use the `UpdateFilters` and `UpdateParameters` host events. +* Parameter overrides don't support dynamic date values, date-time parameters, or time parameters. +* A parameter chip cannot be cleared. +* Overrides don't create chips, and they don't apply to columns or parameters without a chip. +* Scheduled jobs and alerts do not use overrides. +* Skipped overrides aren't reported in the UI or through an embed event. + +== Troubleshooting + +[width="100%", cols="3,6"] +[options='header'] +|===== +|Symptom |Check +|Nothing is applied +a| +* *Filtered Liveboard URLs* is turned on for the Org. +* The keys are `filter` and `parameter`, singular. +* The keys come after the `+#+` route. +* In an embed, the overrides are in `liveboardOverride`, not in the URL. + +|One override is not applied +a| +* A chip exists for that column or parameter, at the scope you targeted. +* The name matches the filter or parameter name in full. +* The value format matches the tables above: ISO dates, one value for parameters, a value inside the parameter's list or range. +* Any prefix matches a model, tab, or group name. + +|An override works for some users but not others +|The user may not have access to the model or column. Overrides on sources a user can't access are skipped for that user. + +|The URL is cut off or a value is wrong +|Encode `&`, `+++`, `%`, `+#+`, and `\` in values. + +|Values disappear after entering edit mode +|Expected. Edit mode always shows the saved Liveboard. + +|A tab or group override is ignored +|Contextual filtering must be turned on. Check the tab and group names. +|===== + +To check what was sent, open the browser's developer tools. On the *Network* tab, find the `LoadPinboardSession` request. The `liveboardOverride` variable in that request shows the overrides ThoughtSpot received. + +== Related information + +* xref:runtime-filters.adoc[Runtime filters] +* xref:runtime-parameters.adoc[Runtime Parameter overrides] +* xref:filters_overview.adoc[Filter types and application layers] +* xref:customize-liveboard-filters.adoc[Customize Liveboard filters] +* xref:LiveboardViewConfig.adoc#_liveboardoverride[SDK reference] \ No newline at end of file diff --git a/modules/ROOT/pages/mcp-integration.adoc b/modules/ROOT/pages/mcp-integration.adoc index 942542efe..8db4ac92e 100644 --- a/modules/ROOT/pages/mcp-integration.adoc +++ b/modules/ROOT/pages/mcp-integration.adoc @@ -14,8 +14,11 @@ ThoughtSpot Spotter MCP Server exposes ThoughtSpot analytics as tools and resour * Automatic discovery of ThoughtSpot MCP tools * Natural language queries and responses * Programmatic creation of Liveboards and visualizations +* Changing existing dashboards, such as rearranging, restyling, and adding charts, text, groups, and tabs * Generating embeddable visualizations * Searching for existing ThoughtSpot content by name, author, tag, or type +* Reading the data behind existing Answers and Liveboards +* Building and editing data models through conversation === Supported integrations ThoughtSpot Spotter MCP Server supports two primary integration options: @@ -161,7 +164,7 @@ To automatically receive every new version of the MCP tools as ThoughtSpot relea Always points to the latest version. * `\https://agent.thoughtspot.app/mcp` + -Currently, this URL points to the baseline version with legacy MCP tools. It will resolve to the latest version in the near future. +Resolves to the latest version. * If you want to pin a specific version, use the URL with the `api-version` parameter and specify the date string: + `\https://agent.thoughtspot.app/mcp?api-version={YYYY-MM-DD}`. For example, `?api-version=2026-05-01` diff --git a/modules/ROOT/pages/mcp-server-changelog.adoc b/modules/ROOT/pages/mcp-server-changelog.adoc index 5c01d63ef..3846d03bd 100644 --- a/modules/ROOT/pages/mcp-server-changelog.adoc +++ b/modules/ROOT/pages/mcp-server-changelog.adoc @@ -24,6 +24,66 @@ This changelog lists the new features, enhancements, and other changes introduce // *Affects:* Developers, Administrators, End Users // ============================================================ +== October 2026 +*API version strings:* `?api-version=2026-10-01`, `?api-version=2026-10-02`, and `?api-version=2026-10-14`. Each release is available on the version string that matches its release date. + +*Upgrade notes*: Existing integrations that use `?api-version=latest`, or a URL without the `api-version` parameter, receive these changes automatically. Integrations that are pinned to an earlier date are not affected until you update the date. The xref:mcp-tool-reference-spotter3.adoc#create_dashboard[create_dashboard] changes in the 2026-10-14 release may break integrations that read its response. + +[.cl-table, cols="1,4", frame=none, grid=none] +|==== +a| +[.cl-label] + +*2026-10-14* + +a| + +[discrete] +==== [.version-badge.breaking]#Breaking# `create_dashboard` MCP tool +The `create_dashboard` tool is now powered by SpotterViz. It can create a dashboard from completed analysis or from a description. SpotterViz shares its plan and chosen data source first, and nothing is built until the user approves. + +* The response is a status object. The tool can return `completed`, `in_progress`, `needs_input`, or `failed`. +* The `note_tile` parameter is removed. +* The `answers` parameter is now optional. +* The `instructions`, `task_id`, and `data_source_id` parameters are added. + +For more information, see xref:mcp-tool-reference-spotter3.adoc#create_dashboard[create_dashboard]. + +--- + +[discrete] +==== `modify_dashboard` MCP tool +The new `modify_dashboard` MCP tool changes an existing dashboard. It can rearrange and restyle content, add charts and explanatory text, and organize charts into groups and tabs. For more information, see xref:mcp-tool-reference-spotter3.adoc#modify_dashboard[modify_dashboard]. + +--- + +[discrete] +==== `get_dashboard_status` MCP tool +The new `get_dashboard_status` MCP tool polls a dashboard task that `create_dashboard` or `modify_dashboard` left in progress. For more information, see xref:mcp-tool-reference-spotter3.adoc#get_dashboard_status[get_dashboard_status]. + +a| +[.cl-label] + +*2026-10-02* + +a| + +[discrete] +==== `get_data` MCP tool +The new `get_data` MCP tool fetches the columns and rows of a saved Answer or Liveboard. Use it with `search_objects` to answer questions from content that your organization already trusts. The tool is available only to users who have the privilege to download data. For more information, see xref:mcp-tool-reference-spotter3.adoc#get_data[get_data]. + +a| +[.cl-label] + +*2026-10-01* + +a| + +[discrete] +==== Model builder MCP tools +The new model builder MCP tools, powered by SpotterModel, let agents build a data model from warehouse tables, or edit an existing model, through conversation. The tools are `create_model_session`, `send_model_message`, `get_model_updates`, and `finalize_model`. The builder proposes a plan for approval, and the model is saved only after the user confirms. The tools are available only to users who have the data management privilege and the privilege to create or edit data models. For more information, see xref:mcp-tool-reference-spotter3.adoc#model-builder-tools[Model builder tools]. + +|==== + == August 2026 *API version string:* `?api-version=2026-05-01` + *Upgrade notes*: Existing integrations using `?api-version=2026-05-01` or `?api-version=latest` will be automatically upgraded. @@ -74,6 +134,7 @@ a| [.cl-label] *2026-05-01* + a| [discrete] @@ -111,7 +172,7 @@ For more information, see xref:mcp-server-spotter3.adoc[MCP Server with Spotter --- [discrete] -==== MCP Server URL changes and API versioning [.version-badge.breaking]#Breaking# +==== [.version-badge.breaking]#Breaking# MCP Server URL changes and API versioning Spotter MCP Server URLs now support date-based versioning, defined using the `?api-version=YYYY-MM-DD` parameter. Ensure that you update the xref:mcp-integration.adoc#_mcp_server_url[MCP Server URL] in your integrations as needed. [NOTE] @@ -123,7 +184,7 @@ Spotter MCP Server URLs now support date-based versioning, defined using the `?a --- [discrete] -==== MCP tools and processing model [.version-badge.breaking]#Breaking# +==== [.version-badge.breaking]#Breaking# MCP tools and processing model The MCP Server URL now points to the Spotter 3-powered MCP tools. With this enhancement, the legacy tools in your existing integrations will be replaced with new tools. If your app uses custom workflows, you must update your integrations to use the new MCP tools or pin a previous version using the `?api-version={YYYY-MM-DD}` parameter in the URL to preserve your existing changes. [discrete] @@ -171,6 +232,6 @@ Supports the following URLs: * `\https://agent.thoughtspot.app/bearer/mcp` (Bearer Token Apps) * `\https://agent.thoughtspot.app/openai/mcp` (OpenAI-compatible clients) -These URLs are deprecated as of the MCP Server 2026-05-01 release. See the xref:mcp-server-changelog.adoc#_mcp_server_url_changes_and_api_versioning_breaking[changelog] for more information. +These URLs are deprecated as of the MCP Server 2026-05-01 release. See the xref:mcp-server-changelog.adoc#_breaking_mcp_server_url_changes_and_api_versioning[changelog] for more information. |==== diff --git a/modules/ROOT/pages/mcp-server-client-connection.adoc b/modules/ROOT/pages/mcp-server-client-connection.adoc index e39893307..a7ed9acb0 100644 --- a/modules/ROOT/pages/mcp-server-client-connection.adoc +++ b/modules/ROOT/pages/mcp-server-client-connection.adoc @@ -52,12 +52,8 @@ For OAuth clients, the following options are available: * `\https://agent.thoughtspot.app/mcp?api-version=latest` + This URL always points to the latest version. -* `\https://agent.thoughtspot.app/mcp` -+ -[NOTE] -==== -Currently, this URL points to the baseline version with legacy MCP tools. It will resolve to the latest version in the near future. During this period, you can switch to `\https://agent.thoughtspot.app/mcp?api-version=latest` to access the latest MCP tools. -==== +* `\https://agent.thoughtspot.app/mcp` + +This URL resolves to the latest version. * `\https://agent.thoughtspot.app/mcp?api-version={YYYY-MM-DD}` + If you want to pin a specific version, use this URL format. diff --git a/modules/ROOT/pages/mcp-server-spotter3.adoc b/modules/ROOT/pages/mcp-server-spotter3.adoc index fe03bf9c8..c5543109f 100644 --- a/modules/ROOT/pages/mcp-server-spotter3.adoc +++ b/modules/ROOT/pages/mcp-server-spotter3.adoc @@ -83,9 +83,8 @@ ThoughtSpot returns the following update types: //// 5. *User generates a dashboard with the results (optional)* + -The user can choose to save answers from the conversation as a ThoughtSpot dashboard. For this workflow, the agent extracts the `answer_id` and `answer_title` from each `answer` type update collected during polling and calls `create_dashboard`. + -ThoughtSpot creates a dashboard and returns a `dashboard_id` and a -`dashboard_url`, a direct link to the dashboard in the ThoughtSpot UI. +The user can choose to save answers from the conversation as a ThoughtSpot dashboard. For this workflow, the agent extracts the `answer_id` and `answer_title` from each `answer` type update collected during polling and calls `create_dashboard`. The agent passes each `answer_title` as the `title` of the answer, and can add `instructions` for layout and style. + +SpotterViz, the ThoughtSpot dashboard designer, organizes and styles the charts and saves them as a dashboard. If the dashboard is not ready, `create_dashboard` returns `status: "in_progress"` and a `task_id`, and the agent polls `get_dashboard_status` until the status changes. When the status is `completed`, the response contains the `dashboard_id` and the `dashboard_url`, a direct link to the dashboard in the ThoughtSpot UI. For more information, see xref:mcp-tool-reference-spotter3.adoc#create_dashboard[create_dashboard]. The other optional tool calls include: @@ -98,6 +97,18 @@ If the user belongs to more than one Org and connects over OAuth, the agent call * *Agent searches for existing content (optional)* + If the user refers to content that already exists, for example, "open the regional sales Liveboard", the agent calls `search_objects` with a search term and optional filters such as object type, author, tag, or verified status. `search_objects` returns a ranked list of matching objects with their metadata and deep links. +* *Agent reads the data behind existing content (optional)* + +After `search_objects` returns an Answer or Liveboard, the agent can call `get_data` with the `object_id` from the result, and the result's `type` as `object_type`, to fetch the columns and rows that the object shows. For a single visualization on a Liveboard, the agent passes the `visualization_id` in `visualization_ids`. The agent answers from the returned rows, which reflect the latest data. `get_data` is available only to users who have the privilege to download data. For more information, see xref:mcp-tool-reference-spotter3.adoc#get_data[get_data]. + +* *User asks for a dashboard from a description (optional)* + +If the user asks for a dashboard before any analysis, or wants one recreated from another tool, the agent calls `create_dashboard` with `instructions` and no `answers`. SpotterViz returns `status: "needs_input"` with the data source it chose and its plan. The agent shows the plan to the user. After the user confirms or asks for changes, the agent calls `create_dashboard` again with the same `task_id` and the user's reply in `instructions`. + +* *User changes an existing dashboard (optional)* + +The agent finds the dashboard GUID with `search_objects`, and calls `modify_dashboard` with the `dashboard_id` and the user's change in `instructions`. If SpotterViz asks a question, the agent shows it to the user and calls `modify_dashboard` again with the same `dashboard_id` and `task_id`. For more information, see xref:mcp-tool-reference-spotter3.adoc#modify_dashboard[modify_dashboard]. + +* *User builds or edits a data model (optional)* + +The agent calls `create_model_session` with a connection GUID for a new model, or a model GUID to edit one. The agent sends instructions with `send_model_message` and polls `get_model_updates` until `is_done` is `true`. The builder proposes a plan first, and the agent shows it to the user and waits for approval. To save, the agent calls `finalize_model` for a summary, and then calls it again with `confirm: true` after the user approves. The model builder tools are available only to users who can create or edit data models. For more information, see xref:mcp-tool-reference-spotter3.adoc#model-builder-tools[Model builder tools]. + == Additional resources * For information about MCP, see the link:https://modelcontextprotocol.io[Model Context Protocol specification, window=_blank]. * For implementation details, see the link:https://github.com/thoughtspot/mcp-server[MCP Server GitHub repository, window=_blank]. diff --git a/modules/ROOT/pages/mcp-tool-reference-spotter3.adoc b/modules/ROOT/pages/mcp-tool-reference-spotter3.adoc index d2f2e6822..097f93e35 100644 --- a/modules/ROOT/pages/mcp-tool-reference-spotter3.adoc +++ b/modules/ROOT/pages/mcp-tool-reference-spotter3.adoc @@ -1,3 +1,4 @@ + = MCP tool reference (Spotter 3) :toc: true :toclevels: 3 @@ -6,7 +7,8 @@ :page-pageid: mcp-tool-reference-spotter3 :page-description: Refer to this guide to learn which MCP tools are supported, input parameters used for tool calls, and the response format. -The ThoughtSpot Spotter Model Context Protocol (MCP) Server integration exposes tools for running natural language analytics queries and searching existing ThoughtSpot content. The core analytics pattern is: **create a session** → **send a message** → **poll for updates**. The `search_objects` tool is independent of this workflow and can be called at any time. +Analysis, dashboard creation, and model building each follow the same pattern, **start the work** → **poll for updates** until it finishes. +The analysis workflow is **create a session** → **send a message** → **poll for updates**. The `search_objects` and `get_data` tools are independent of these workflows and can be called at any time. * <> + Start an analytical session. @@ -15,9 +17,23 @@ Send a natural language question or follow-up. * <> + Poll for streamed responses. * <> + -Create a dashboard from session answers. +Create a dashboard from session answers or from a description. +* <> + +Change an existing dashboard. +* <> + +Poll a dashboard task that is still running. * <> + Search for existing ThoughtSpot content by name or description. +* <> + +Fetch the columns and rows of a saved Answer or Liveboard. +* <> + +Start a session to build or edit a data model. +* <> + +Send an instruction to the model builder or answer one of its questions. +* <> + +Poll for the model builder's progress. +* <> + +Review and save the model. * <> + List the Orgs your account can access. (OAuth connections on Org-enabled instances only) * <> + @@ -25,6 +41,14 @@ Switch your active Org for the current session. (OAuth connections on Org-enable * <> + Verify that the MCP Server is reachable. +[NOTE] +==== +* The new and updated tools are available by default on the MCP server endpoint with `?api-version=latest`. To pin your integration to the release that includes them, use the release date, for example `?api-version=2026-10-14`. For information about the available API versions and release dates, see the xref:mcp-server-changelog.adoc[MCP Server changelog]. +* The server lists `get_data` only for users who have the privilege to download data. +* The server lists the model builder tools only for users who have the data management privilege and the privilege to create or edit data models. +==== + + [#create_analysis_session] == create_analysis_session Start an analytical session with ThoughtSpot's analytics agent. This is the required first step before sending any questions. @@ -32,7 +56,9 @@ Start an analytical session with ThoughtSpot's analytics agent. This is the requ Sessions are conversational. Once created, you can send multiple follow-up questions to the same session without calling `create_analysis_session` again. === Input parameter -The `data_source_id` is optional. Provide this when the user has specified or confirmed a data source, or when context makes a particular source obvious. Omit to let ThoughtSpot automatically select the most relevant source based on the question. +The `data_source_id` is optional. Provide this when the user has specified or confirmed a data source, or when context makes a particular source obvious. Omit to let ThoughtSpot automatically select the most relevant source based on the question. If the instance does not support automatic selection, the tool returns an error that asks for a data source. Find the GUID with xref:mcp-tool-reference-spotter3.adoc#search_objects[search_objects], using `types: ["WORKSHEET"]`. If you have only the data source name, you can pass it in the `additional_context` field of `send_session_message` instead. + +The `data_source_id` must be a GUID in the format `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`. === Example call @@ -92,7 +118,7 @@ __Optional__|Can be used to provide external information relating to the questio [source,javascript] ---- const sendMessage = await callMCPTool("send_session_message", { - analytical_session_id: "sess_abc123", // Session ID from create_analysis_session. + analytical_session_id: "session-guid-abc123", // Session ID from create_analysis_session. message: "What were total sales by region last quarter?", // User's natural language question. additional_context: "The user's fiscal year starts in April. " + "Focus on underperforming regions only." // Optional. External information to influence the analysis. @@ -104,10 +130,10 @@ const sendMessage = await callMCPTool("send_session_message", { call_mcp_tool( "send_session_message", { - "analytical_session_id": "sess_abc123", # Session ID from create_analysis_session. + "analytical_session_id": "session-guid-abc123", # Session ID from create_analysis_session. "message": "What were total sales by region last quarter?", # User's natural language question. "additional_context": ( # Optional. External information - "The user's fiscal year starts in April." + "The user's fiscal year starts in April. " "Focus on underperforming regions only." # to influence the analysis. ), }, @@ -131,6 +157,7 @@ call_mcp_tool( * After a successful send, immediately begin polling with `get_session_updates`. * Do not send a second message until `get_session_updates` returns `is_done: true`. The agent processes one message at a time per session. * To ask a follow-up question, reuse the same `analytical_session_id`. There is no need to create a new session. +* Do not send a dashboard request through this tool. To create a dashboard, use xref:mcp-tool-reference-spotter3.adoc#create_dashboard[create_dashboard]. ==== [#get_session_updates] @@ -146,7 +173,7 @@ Send the `analytical_session_id` to specify the session to retrieve updates for. [div tabbed-code] -- -[source,typescript] +[source,javascript] ---- const updates = await callMCPTool("get_session_updates", { analytical_session_id: "session-guid-abc123" // Session ID from the `create_analysis_session` call. @@ -157,7 +184,7 @@ const updates = await callMCPTool("get_session_updates", { ---- call_mcp_tool( "get_session_updates", - {"analytical_session_id": "sess_abc123"}, # Session ID from create_analysis_session. + {"analytical_session_id": "session-guid-abc123"}, # Session ID from create_analysis_session. ) ---- -- @@ -180,9 +207,9 @@ Render these immediately so the user sees progress in real time. |A fragment of Spotter's narration. Concatenate chunks to form the complete narration string. When `is_thinking` is `false`, the chunk is part of the final answer narration, not an intermediate thinking step. -|`answer`|`false` +|`answer`|`true` or `false` |The analytical result. Contains a title, the ThoughtSpot search query used, an embeddable `iframe_url`, and an `answer_id` for creating dashboards. -Exactly one `answer` update is present per question. +The same answer can arrive twice in one response, once with `is_thinking: true` and once with `is_thinking: false`. Both updates carry the same `answer_id`. Render the answer once, and use the `answer_id` to remove duplicates. |==== Poll returning intermediate updates: @@ -223,6 +250,15 @@ Poll returning the final answer: "type": "text_chunk", "text": "Running the query against the sales data model..." }, + { + "is_thinking": true, + "type": "answer", + "answer_id": "{\"session_id\":\"1a3d...\",\"gen_no\":2}", + "answer_title": "Total sales by region", + "answer_data_source_id": "cd252e5c-...", + "answer_query": "[sales] [region]", + "iframe_url": "https://your-instance.thoughtspot.cloud/?tsmcp=true#/embed/conv-assist-answer?..." + }, { "is_thinking": false, "type": "text_chunk", @@ -255,8 +291,8 @@ A typical response sequence might look like: . Step headings: `step_notification` updates announcing each step Spotter is about to start. . Thinking narration: `text_chunk` updates with `is_thinking: true` describing what Spotter is doing. -. Clarifications or caveats: `text_chunk` updates with `is_thinking: false` explaining assumptions, filters applied, or potential ambiguities in the question. -. The final answer: one or more `answer` updates containing the visualization title, the underlying query, and the embeddable iframe URL. +. The answer: an `answer` update containing the visualization title, the underlying query, the `answer_id`, and the embeddable iframe URL. The same answer can arrive again with `is_thinking: false`. +. Final narration: `text_chunk` updates with `is_thinking: false`. This text can explain assumptions, filters applied, or ambiguities in the question, and can include a Markdown summary of the results, such as key takeaways. It can arrive before and after the `answer` update, so the `answer` is not always the last update. This means a complete response might span 5–20+ `get_session_updates` calls and contain many `session_update` objects before `is_done` becomes `true`. All of this content, the thinking, the narration, and the final answer, should be accumulated and presented together to give the user the full picture. @@ -265,7 +301,7 @@ Includes three types, `step_notification`, `text_chunk`, `answer`. Flat, fixed k [source,json] ---- -{"is_thinking":true,"type":"step_notification","text":"Searching for Datasets"} +{"is_thinking":true,"type":"step_notification","text":"Searching data models"} {"is_thinking":true,"type":"text_chunk","text":" to find the most relevant dataset..."} {"is_thinking":false,"type":"answer", "answer_id":"{\"session_id\":\"1a3d...\",\"gen_no\":2}", @@ -286,6 +322,8 @@ Each item in the `session_updates` list is a `session_update` object. The `type` |`"step_notification"`|A short heading announcing the step Spotter is about to start, such as `Searching data models` or `Running query`. |`"text_chunk"` | A streaming fragment of Spotter's narration. Concatenate all chunks to reconstruct the full text. |`"answer"`|Populates `answer_title`, `answer_query`, `answer_data_source_id`, `iframe_url` fields. A data visualization result with a title, the underlying query, and an embeddable URL. +|`is_thinking`|__Boolean__|`true` for intermediate reasoning (`step_notification` updates and thinking `text_chunk` updates). `false` for the final narration and the answer. +|`answer_id`|__String__|Identifier of the answer. Present only when `type` is `"answer"`. Pass it as is to `create_dashboard`. |`text`|__String__|The text content of the message. Present only when `type` is `"step_notification"` or `"text_chunk"`. For `"text_chunk"` updates, concatenate all chunks to form the complete message. |`answer_title`|__String__|A human-readable title describing what the answer shows. Present only when `type` is `"answer"`. |`answer_data_source_id`|__String__|GUID of the data source used to generate the answer. Present only when `type` is `"answer"`. @@ -328,48 +366,70 @@ The full response does not include a preconstructed `iframe_url`. You must build For the complete event schema, see the xref:spotter-agent-apis.adoc#_api_response_2[Spotter Agent API, window=_blank]. + [#create_dashboard] == create_dashboard -Create a ThoughtSpot dashboard from answers generated in an analysis session. -* Call this only after `get_session_updates` returns `is_done: true`, because you need the `answer_id` values from completed answer updates. -* Collect all updates where type is answer across every poll of `get_session_updates`. Each one produces an `answer_id` you can include in the dashboard. -* The `note_tile` should summarize the full analysis. It is the first thing a viewer sees on the dashboard. -* Multiple answers from the same session or across multiple sessions can be combined into a single dashboard. +Create a ThoughtSpot dashboard and save it, so that users can revisit or share the results. SpotterViz, the ThoughtSpot dashboard designer, builds the dashboard. Use this tool when a user asks for a dashboard or Liveboard, asks to save analysis results, or wants to recreate a dashboard from another tool. + +You can call the tool in two ways: + +. *From completed analysis*: Pass `answers` collected from `get_session_updates`, with optional `instructions` for layout and style. SpotterViz organizes, styles, and saves the charts. Call this only after `get_session_updates` returns `is_done: true`, because you need the answer ID values from completed answer updates. +. *From a description*: Pass `instructions` that describe the dashboard. Use this when the user asks for a dashboard before any analysis, or wants one recreated from a specification. In this case, do not run the analysis first. SpotterViz replies with `needs_input`, which contains the data source it chose and its plan. Nothing is built until the user confirms the plan. + +Dashboard creation can take a few minutes, so the call can return `in_progress` before the dashboard is ready. === Input parameters +Pass `answers`, `instructions`, or both. [cols="2,4", options="header"] |==== |Field|Description -|`title` |Required. Title of the dashboard to be created. -|`answers`|Required. List of answer objects to add to the dashboard. Each answer requires an `answer_id` (from `get_session_updates` where `type` is `answer`) and a `title`. -|`note_tile`| Required. An HTML summary of the analysis and answers, rendered as a styled tile on the dashboard. Must be a single line with no line breaks. Use `
` for spacing within the HTML. Include emojis, colors, and a `"Generated on