Skip to content

Commit 579ad06

Browse files
@W-21748754: Add get-custom-view-data tool (#308)
* Add get-custom-view-data tool * Update docs * Remove cache write in _isCustomViewAllowed * Fix compile errors * Fix tests * Remove reference to get-custom-view-image
1 parent 5a8ea5d commit 579ad06

23 files changed

Lines changed: 753 additions & 43 deletions

docs/docs/intro.md

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,25 +18,26 @@ Tableau's official MCP Server. Helping Agents see and understand data.
1818

1919
## Tool List
2020

21-
| **Tool** | **Description** |
22-
| --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
23-
| [list-datasources](tools/data-qna/list-datasources.md) | Retrieves a list of published data sources from a specified Tableau site ([REST API][query]) |
24-
| [list-workbooks](tools/workbooks/list-workbooks.md) | Retrieves a list of workbooks from a specified Tableau site ([REST API][list-workbooks]) |
25-
| [list-views](tools/views/list-views.md) | Retrieves a list of views from a specified Tableau site ([REST API][list-views]) |
26-
| [list-custom-views](tools/views/list-custom-views.md) | Retrieves a list of custom views for a specified Tableau workbook ([REST API][list-custom-views]) |
27-
| [get-datasource-metadata](tools/data-qna/get-datasource-metadata.md) | Fetches field metadata for the specified datasource ([Metadata API][meta] & [VDS API][vds]) |
28-
| [get-workbook](tools/workbooks/get-workbook.md) | Retrieves information on a workbook from a specified Tableau site ([REST API][get-workbook]) |
29-
| [get-view-data](tools/views/get-view-data.md) | Retrieves data in CSV format for the specified view in a Tableau workbook ([REST API][get-view-data]) |
30-
| [get-view-image](tools/views/get-view-image.md) | Retrieves an image for the specified view in a Tableau workbook ([REST API][get-view-image]) |
31-
| [query-datasource](tools/data-qna/query-datasource.md) | Run a Tableau VizQL query ([VDS API][vds]) |
32-
| [list-all-pulse-metric-definitions](tools/pulse/list-all-pulse-metric-definitions.md) | List All Pulse Metric Definitions ([Pulse API][pulse]) |
33-
| [list-pulse-metric-definitions-from-definition-ids](tools/pulse/list-pulse-metric-definitions-from-definition-ids.md) | List Pulse Metric Definitions from Metric Definition IDs ([Pulse API][pulse]) |
34-
| [list-pulse-metrics-from-metric-definition-id](tools/pulse/list-pulse-metrics-from-metric-definition-id.md) | List Pulse Metrics from Metric Definition ID ([Pulse API][pulse]) |
35-
| [list-pulse-metrics-from-metric-ids](tools/pulse/list-pulse-metrics-from-metric-ids.md) | List Pulse Metrics from Metric IDs ([Pulse API][pulse]) |
36-
| [list-pulse-metric-subscriptions](tools/pulse/list-pulse-metric-subscriptions.md) | List Pulse Metric Subscriptions for Current User ([Pulse API][pulse]) |
37-
| [generate-pulse-metric-value-insight-bundle](tools/pulse/generate-pulse-metric-value-insight-bundle.md) | Generate Pulse Metric Value Insight Bundle ([Pulse API][pulse]) |
38-
| [generate-pulse-insight-brief](tools/pulse/generate-pulse-insight-brief.md) | Generate AI-powered Pulse Insight Brief (Discover) ([Pulse API][pulse]) |
39-
| [search-content](tools/content-exploration/search-content.md) | Searches for content in a Tableau site ([Content Exploration API][content-exploration]) |
21+
| **Tool** | **Description** |
22+
| --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
23+
| [list-datasources](tools/data-qna/list-datasources.md) | Retrieves a list of published data sources from a specified Tableau site ([REST API][query]) |
24+
| [list-workbooks](tools/workbooks/list-workbooks.md) | Retrieves a list of workbooks from a specified Tableau site ([REST API][list-workbooks]) |
25+
| [list-views](tools/views/list-views.md) | Retrieves a list of views from a specified Tableau site ([REST API][list-views]) |
26+
| [list-custom-views](tools/views/list-custom-views.md) | Retrieves a list of custom views for a specified Tableau workbook ([REST API][list-custom-views]) |
27+
| [get-datasource-metadata](tools/data-qna/get-datasource-metadata.md) | Fetches field metadata for the specified datasource ([Metadata API][meta] & [VDS API][vds]) |
28+
| [get-workbook](tools/workbooks/get-workbook.md) | Retrieves information on a workbook from a specified Tableau site ([REST API][get-workbook]) |
29+
| [get-view-data](tools/views/get-view-data.md) | Retrieves data in CSV format for the specified view in a Tableau workbook ([REST API][get-view-data]) |
30+
| [get-view-image](tools/views/get-view-image.md) | Retrieves an image for the specified view in a Tableau workbook ([REST API][get-view-image]) |
31+
| [get-custom-view-data](tools/views/get-custom-view-data.md) | Retrieves data in CSV format for the specified custom view in a Tableau workbook ([REST API][get-custom-view-data]) |
32+
| [query-datasource](tools/data-qna/query-datasource.md) | Run a Tableau VizQL query ([VDS API][vds]) |
33+
| [list-all-pulse-metric-definitions](tools/pulse/list-all-pulse-metric-definitions.md) | List All Pulse Metric Definitions ([Pulse API][pulse]) |
34+
| [list-pulse-metric-definitions-from-definition-ids](tools/pulse/list-pulse-metric-definitions-from-definition-ids.md) | List Pulse Metric Definitions from Metric Definition IDs ([Pulse API][pulse]) |
35+
| [list-pulse-metrics-from-metric-definition-id](tools/pulse/list-pulse-metrics-from-metric-definition-id.md) | List Pulse Metrics from Metric Definition ID ([Pulse API][pulse]) |
36+
| [list-pulse-metrics-from-metric-ids](tools/pulse/list-pulse-metrics-from-metric-ids.md) | List Pulse Metrics from Metric IDs ([Pulse API][pulse]) |
37+
| [list-pulse-metric-subscriptions](tools/pulse/list-pulse-metric-subscriptions.md) | List Pulse Metric Subscriptions for Current User ([Pulse API][pulse]) |
38+
| [generate-pulse-metric-value-insight-bundle](tools/pulse/generate-pulse-metric-value-insight-bundle.md) | Generate Pulse Metric Value Insight Bundle ([Pulse API][pulse]) |
39+
| [generate-pulse-insight-brief](tools/pulse/generate-pulse-insight-brief.md) | Generate AI-powered Pulse Insight Brief (Discover) ([Pulse API][pulse]) |
40+
| [search-content](tools/content-exploration/search-content.md) | Searches for content in a Tableau site ([Content Exploration API][content-exploration]) |
4041

4142
[query]:
4243
https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_data_sources.htm#query_data_sources
@@ -52,6 +53,8 @@ Tableau's official MCP Server. Helping Agents see and understand data.
5253
https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#query_view_data
5354
[get-view-image]:
5455
https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#query_view_image
56+
[get-custom-view-data]:
57+
https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#get_custom_view_data
5558
[meta]: https://help.tableau.com/current/api/metadata_api/en-us/index.html
5659
[vds]: https://help.tableau.com/current/api/vizql-data-service/en-us/index.html
5760
[pulse]: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_pulse.htm
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
sidebar_position: 5
3+
---
4+
5+
# Get Custom View Data
6+
7+
Retrieves data in comma separated value (CSV) format for the specified custom view in a Tableau
8+
workbook.
9+
10+
## APIs called
11+
12+
- [Get Custom View Data](https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#get_custom_view_data)
13+
- [Get Custom View](https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#get_custom_view)
14+
and
15+
[Get View](https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_workbooks_and_views.htm#get_view)
16+
(if applicable [tool scoping](../../configuration/mcp-config/tool-scoping.md) is enabled)
17+
18+
## Required arguments
19+
20+
### `customViewId`
21+
22+
The ID of the custom view, potentially retrieved by the [List Custom Views](list-custom-views.md)
23+
tool.
24+
25+
Example: `1db3a121-51ac-4435-b533-3053e698dfc8`
26+
27+
## Optional arguments
28+
29+
### `viewFilters`
30+
31+
Map of view filter field names to values. `vf_` prefix for field names is optional and will be added
32+
automatically when building the view filter query.
33+
34+
Example: `{ "year": "2017" }`
35+
36+
## Example result
37+
38+
```
39+
Country/Region,State/Province,Profit Ratio,Latitude (generated),Longitude (generated)
40+
Canada,Ontario,26.8%,50.94,-84.75
41+
```

docs/docs/tools/views/get-view-data.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ The ID of the view, potentially retrieved by the [List Views](list-views.md) or
2121

2222
Example: `9460abfe-a6b2-49d1-b998-39e1ebcc55ce`
2323

24+
## Optional arguments
25+
26+
### `viewFilters`
27+
28+
Map of view filter field names to values. `vf_` prefix for field names is optional and will be added
29+
automatically when building the view filter query.
30+
31+
Example: `{ "year": "2017" }`
32+
2433
## Example result
2534

2635
```

docs/docs/tools/views/get-view-image.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,34 @@ resolution and aspect ratio.
3939

4040
Example: `1200`
4141

42+
<hr />
43+
4244
### `format`
4345

4446
The format of the image. Default: `PNG`
4547

4648
- **`PNG`** (default): Raster image format. Works with all Tableau Server versions.
47-
- **`SVG`**: Vector graphics format. Scalable and smaller file size. **Requires Tableau Server 2026.2.0+ (REST API v3.29+)**.
49+
- **`SVG`**: Vector graphics format. Scalable and smaller file size. **Requires Tableau Server
50+
2026.2.0+ (REST API v3.29+)**.
4851

4952
**Choosing a format:**
50-
- Prefer `PNG` when the image will be **analyzed or interpreted** (e.g. answering questions about the data in the viz).
51-
- Prefer `SVG` when the image will be **displayed to the user** (e.g. embedding or rendering the viz in a response).
53+
54+
- Prefer `PNG` when the image will be **analyzed or interpreted** (e.g. answering questions about
55+
the data in the viz).
56+
- Prefer `SVG` when the image will be **displayed to the user** (e.g. embedding or rendering the viz
57+
in a response).
5258

5359
Example: `SVG`
5460

61+
<hr />
62+
63+
### `viewFilters`
64+
65+
Map of view filter field names to values. `vf_` prefix for field names is optional and will be added
66+
automatically when building the view filter query.
67+
68+
Example: `{ "year": "2017" }`
69+
5570
## Example result
5671

5772
![Superstore View Image](./superstore.png)

src/logging/secretMask.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ describe('secretMask', () => {
8888
const maskedResponse = maskResponse({
8989
status: 200,
9090
baseUrl: 'https://example.com',
91+
params: {},
9192
url: '/api/v1/users',
9293
headers: { 'Some-Header': 'hamburgers' },
9394
data: {
@@ -100,6 +101,7 @@ describe('secretMask', () => {
100101
status: 200,
101102
baseUrl: 'https://example.com',
102103
url: '/api/v1/users',
104+
params: {},
103105
headers: { 'Some-Header': 'hamburgers' },
104106
data: {
105107
credentials: '<redacted>',
@@ -137,6 +139,7 @@ describe('secretMask', () => {
137139
const maskedResponse = maskResponse({
138140
status: 200,
139141
baseUrl: 'https://example.com',
142+
params: {},
140143
url: '/api/v1/users',
141144
headers: { 'Some-Header': 'hamburgers' },
142145
data: {
@@ -149,6 +152,7 @@ describe('secretMask', () => {
149152
status: 200,
150153
baseUrl: 'https://example.com',
151154
url: '/api/v1/users',
155+
params: {},
152156
});
153157
});
154158

@@ -172,6 +176,7 @@ describe('secretMask', () => {
172176
const response: ResponseInterceptorConfig = {
173177
status: 200,
174178
baseUrl: 'https://example.com',
179+
params: {},
175180
url: '/api/v1/users',
176181
headers: { 'Some-Header': 'hamburgers' },
177182
// Functions can't be cloned by the structured clone algorithm.

src/restApiInstance.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ describe('restApiInstance', () => {
253253
status: 200,
254254
url: '/api/test',
255255
baseUrl: mockHost,
256+
params: {},
256257
headers: {},
257258
data: {},
258259
};

src/restApiInstance.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,8 @@ function logResponse(
291291
const url = new URL(
292292
`${maskedResponse.baseUrl.replace(/\/$/, '')}/${maskedResponse.url?.replace(/^\//, '') ?? ''}`,
293293
);
294-
if (response.request?.params && Object.keys(response.request.params).length > 0) {
295-
url.search = new URLSearchParams(response.request.params).toString();
294+
if (response.params && Object.keys(response.params).length > 0) {
295+
url.search = new URLSearchParams(response.params).toString();
296296
}
297297
const messageObj = {
298298
type: 'response',

src/sdks/tableau/apis/viewsApi.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,44 @@ const listCustomViewsEndpoint = makeEndpoint({
3636
}),
3737
});
3838

39+
const getCustomViewEndpoint = makeEndpoint({
40+
method: 'get',
41+
path: '/sites/:siteId/customviews/:customViewId',
42+
alias: 'getCustomView',
43+
description: 'Gets the details of a specified custom view.',
44+
response: z.object({ customView: customViewSchema }),
45+
});
46+
47+
const getCustomViewDataEndpoint = makeEndpoint({
48+
method: 'get',
49+
path: '/sites/:siteId/customviews/:customViewId/data',
50+
alias: 'getCustomViewData',
51+
description:
52+
'Returns a specified custom view rendered as data in comma separated value (CSV) format.',
53+
parameters: [
54+
{
55+
name: 'dummy',
56+
type: 'Query',
57+
schema: z.never().optional(),
58+
description: 'Dummy parameter to allow arbitrary filter parameters to be provided',
59+
},
60+
],
61+
response: z.string(),
62+
});
63+
3964
const queryViewDataEndpoint = makeEndpoint({
4065
method: 'get',
4166
path: '/sites/:siteId/views/:viewId/data',
4267
alias: 'queryViewData',
4368
description: 'Returns a specified view rendered as data in comma separated value (CSV) format.',
69+
parameters: [
70+
{
71+
name: 'dummy',
72+
type: 'Query',
73+
schema: z.never().optional(),
74+
description: 'Dummy parameter to allow arbitrary filter parameters to be provided',
75+
},
76+
],
4477
response: z.string(),
4578
});
4679

@@ -141,6 +174,8 @@ const queryViewsForSiteEndpoint = makeEndpoint({
141174
const viewsApi = makeApi([
142175
getViewEndpoint,
143176
listCustomViewsEndpoint,
177+
getCustomViewEndpoint,
178+
getCustomViewDataEndpoint,
144179
queryViewDataEndpoint,
145180
queryViewImageEndpoint,
146181
queryViewsForWorkbookEndpoint,

src/sdks/tableau/interceptors.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ export type ResponseInterceptorConfig = {
2020
url: string;
2121
headers: Record<string, any>;
2222
// AxiosHeaders is a complex class, overwrite it for simplicity.
23-
} & Omit<AxiosResponseInterceptorConfig, 'headers' | 'statusText' | 'config'>;
23+
} & Omit<AxiosResponseInterceptorConfig, 'headers' | 'statusText' | 'config'> & {
24+
params: AxiosResponseInterceptorConfig['config']['params'];
25+
};
2426

2527
export function getRequestInterceptorConfig(
2628
config: AxiosRequestInterceptorConfig,
@@ -39,6 +41,7 @@ export function getResponseInterceptorConfig(
3941
): Omit<ResponseInterceptorConfig, 'baseUrl'> {
4042
return {
4143
url: response.config.url ?? 'UNKNOWN URL',
44+
params: response.config.params,
4245
status: response.status,
4346
headers: response.headers,
4447
data: response.data,

0 commit comments

Comments
 (0)