query-view.ts•4.71 kB
/**
* MCP Tool: tableau_query_view
*
* Purpose: Export view data as CSV or JSON with optional row limiting
* Parameters:
* - viewId (required): View identifier
* - format (optional): 'csv' or 'json' (default: 'csv')
* - maxRows (optional): Maximum rows to return
* Returns: Data in requested format
*/
import { z } from 'zod';
import { TableauClient } from '../tableau-client.js';
// Zod schema for input validation
export const QueryViewArgsSchema = z.object({
viewId: z.string().min(1).describe('View identifier (required)'),
format: z.enum(['csv', 'json']).optional().default('csv').describe('Output format: csv or json (default: csv)'),
maxRows: z.number().int().positive().optional().describe('Maximum number of rows to return'),
});
export type QueryViewArgs = z.infer<typeof QueryViewArgsSchema>;
/**
* Handler for tableau_query_view tool
*
* Exports view data in CSV or JSON format with optional row limiting.
* Useful for data analysis, reporting, and integration with other tools.
*
* @param args - Tool arguments (viewId, format, maxRows)
* @param tableauClient - Initialized Tableau client instance
* @returns Formatted response with view data or error
*/
export async function queryViewHandler(
args: QueryViewArgs,
tableauClient: TableauClient
): Promise<{ content: Array<{ type: string; text: string }> }> {
try {
// Validate viewId is provided
if (!args.viewId || args.viewId.trim() === '') {
return {
content: [{
type: 'text',
text: 'Error: viewId is required and cannot be empty.'
}]
};
}
const format = args.format || 'csv';
// Call TableauClient to query view data
const data = await tableauClient.queryViewData(
args.viewId,
format,
args.maxRows
);
// Format response based on output format
let responseText: string;
if (format === 'json') {
// For JSON, ensure it's properly formatted
const jsonData = typeof data === 'string' ? JSON.parse(data) : data;
const rowCount = Array.isArray(jsonData) ? jsonData.length :
(jsonData.data ? jsonData.data.length : 'unknown');
responseText = `View data exported successfully in JSON format.\n` +
`Rows returned: ${rowCount}\n` +
(args.maxRows ? `Row limit: ${args.maxRows}\n` : '') +
`\nJSON Data:\n${JSON.stringify(jsonData, null, 2)}`;
} else {
// For CSV, count lines
const lines = data.toString().split('\n').filter(line => line.trim());
const rowCount = Math.max(0, lines.length - 1); // Subtract header row
responseText = `View data exported successfully in CSV format.\n` +
`Rows returned: ${rowCount}\n` +
(args.maxRows ? `Row limit: ${args.maxRows}\n` : '') +
`\nCSV Data:\n${data}`;
}
return {
content: [{
type: 'text',
text: responseText
}]
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
// Provide helpful error messages
if (errorMessage.includes('404') || errorMessage.includes('not found')) {
return {
content: [{
type: 'text',
text: `Error: View with ID '${args.viewId}' not found. Please verify the view ID is correct and you have access to it.`
}]
};
}
if (errorMessage.includes('timeout') || errorMessage.includes('too large')) {
return {
content: [{
type: 'text',
text: `Error: Query timed out or data is too large. Try using the maxRows parameter to limit the result set. Error: ${errorMessage}`
}]
};
}
return {
content: [{
type: 'text',
text: `Error querying view data: ${errorMessage}`
}]
};
}
}
// Tool metadata for MCP server registration
export const queryViewTool = {
name: 'tableau_query_view',
description: 'Export view (worksheet/dashboard) data as CSV or JSON. Supports optional row limiting for large datasets. Use this to extract underlying data from Tableau visualizations for analysis, reporting, or integration with other systems.',
inputSchema: {
type: 'object',
properties: {
viewId: {
type: 'string',
description: 'Required: View identifier to query data from'
},
format: {
type: 'string',
enum: ['csv', 'json'],
description: 'Optional: Output format (csv or json). Default: csv'
},
maxRows: {
type: 'number',
description: 'Optional: Maximum number of rows to return (useful for large datasets)'
}
},
required: ['viewId']
}
};