Skip to main content
Glama
listViews.ts5.84 kB
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import { Ok } from 'ts-results-es'; import { z } from 'zod'; import { BoundedContext, getConfig } from '../../config.js'; import { useRestApi } from '../../restApiInstance.js'; import { View } from '../../sdks/tableau/types/view.js'; import { Server } from '../../server.js'; import { getTableauAuthInfo } from '../../server/oauth/getTableauAuthInfo.js'; import { paginate } from '../../utils/paginate.js'; import { genericFilterDescription } from '../genericFilterDescription.js'; import { ConstrainedResult, Tool } from '../tool.js'; import { parseAndValidateViewsFilterString } from './viewsFilterUtils.js'; const paramsSchema = { filter: z.string().optional(), pageSize: z.number().gt(0).optional(), limit: z.number().gt(0).optional(), }; export const getListViewsTool = (server: Server): Tool<typeof paramsSchema> => { const listViewsTool = new Tool({ server, name: 'list-views', description: ` Retrieves a list of views on a Tableau site including their metadata such as name, owner, and the workbook they are found in. Supports optional filtering via field:operator:value expressions (e.g., name:eq:Overview) for precise and flexible view discovery. Use this tool when a user requests to list, search, or filter Tableau views on a site. **Supported Filter Fields and Operators** | Field | Operators | |---------------------|----------------------| | caption | eq, in | | contentUrl | eq, in | | createdAt | eq, gt, gte, lt, lte | | favoritesTotal | eq, gt, gte, lt, lte | | fields | eq, in | | hitsTotal | eq, gt, gte, lt, lte | | name | eq, in | | ownerDomain | eq, in | | ownerEmail | eq, in | | ownerName | eq, in | | projectName | eq, in | | sheetNumber | eq, gt, gte, lt, lte | | sheetType | eq, in | | tags | eq, in | | title | eq, in | | updatedAt | eq, gt, gte, lt, lte | | viewUrlname | eq, in | | workbookDescription | eq, in | | workbookName | eq, in | ${genericFilterDescription} **Example Usage:** - List all views on a site - List views with the name "Overview": filter: "name:eq:Overview" - List views in the "Finance" project: filter: "projectName:eq:Finance" - List views created after January 1, 2023: filter: "createdAt:gt:2023-01-01T00:00:00Z" - List views with the name "Overview" in the "Finance" project and created after January 1, 2023: filter: "name:eq:Overview,projectName:eq:Finance,createdAt:gt:2023-01-01T00:00:00Z"`, paramsSchema, annotations: { title: 'List Views', readOnlyHint: true, openWorldHint: false, }, callback: async ( { filter, pageSize, limit }, { requestId, authInfo }, ): Promise<CallToolResult> => { const config = getConfig(); const validatedFilter = filter ? parseAndValidateViewsFilterString(filter) : undefined; return await listViewsTool.logAndExecute({ requestId, authInfo, args: {}, callback: async () => { return new Ok( await useRestApi({ config, requestId, server, jwtScopes: ['tableau:content:read'], authInfo: getTableauAuthInfo(authInfo), callback: async (restApi) => { const views = await paginate({ pageConfig: { pageSize, limit: config.maxResultLimit ? Math.min(config.maxResultLimit, limit ?? Number.MAX_SAFE_INTEGER) : limit, }, getDataFn: async (pageConfig) => { const { pagination, views: data } = await restApi.viewsMethods.queryViewsForSite({ siteId: restApi.siteId, filter: validatedFilter ?? '', includeUsageStatistics: true, pageSize: pageConfig.pageSize, pageNumber: pageConfig.pageNumber, }); return { pagination, data }; }, }); return views; }, }), ); }, constrainSuccessResult: (views) => constrainViews({ views, boundedContext: config.boundedContext }), }); }, }); return listViewsTool; }; export function constrainViews({ views, boundedContext, }: { views: Array<View>; boundedContext: BoundedContext; }): ConstrainedResult<Array<View>> { if (views.length === 0) { return { type: 'empty', message: 'No views were found. Either none exist or you do not have permission to view them.', }; } const { projectIds, workbookIds } = boundedContext; if (projectIds) { views = views.filter((view) => (view.project?.id ? projectIds.has(view.project.id) : false)); } if (workbookIds) { views = views.filter((view) => (view.workbook?.id ? workbookIds.has(view.workbook.id) : false)); } if (views.length === 0) { return { type: 'empty', message: [ 'The set of allowed views that can be queried is limited by the server configuration.', 'While views were found, they were all filtered out by the server configuration.', ].join(' '), }; } return { type: 'success', result: views, }; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/datalabs89/tableau-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server