list
Retrieve complete lists of Metabase resources like cards, dashboards, tables, databases, or collections for efficient browsing and overview purposes. Supports pagination for large datasets.
Instructions
Fetch all records for a single Metabase resource type with highly optimized responses for overview purposes. Retrieves complete lists of cards, dashboards, tables, databases, or collections. Returns only essential identifier fields for efficient browsing and includes intelligent caching for performance. Supports pagination for large datasets exceeding token limits.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| model | Yes | Model type to list ALL records for. Supported models: cards (all questions/queries), dashboards (all dashboards), tables (all database tables), databases (all connected databases), collections (all folders/collections). Only one model type allowed per request for optimal performance. | |
| offset | No | Starting offset for pagination. Use with limit for paginating through large datasets that exceed token limits. | |
| limit | No | Maximum number of items to return per page. Maximum 1000 items per page. Use with offset for pagination. |
Implementation Reference
- src/handlers/list/index.ts:20-212 (handler)The main handler function that executes the 'list' tool logic: validates parameters, fetches data from Metabase API for specified model, applies optimizations, handles pagination, and formats the response with usage guidance.export async function handleList( request: CallToolRequest, requestId: string, apiClient: MetabaseApiClient, logDebug: (message: string, data?: unknown) => void, logInfo: (message: string, data?: unknown) => void, logWarn: (message: string, data?: unknown, error?: Error) => void, logError: (message: string, data?: unknown) => void ) { const { model, offset, limit } = request.params?.arguments || {}; // Validate required parameters if (!model || typeof model !== 'string') { logWarn('Missing or invalid model parameter in list request', { requestId }); throw new McpError( ErrorCode.InvalidParams, 'Model parameter is required and must be a string. Supported models: cards, dashboards, tables, databases, collections' ); } // Validate model type with case insensitive handling const supportedModels = ['cards', 'dashboards', 'tables', 'databases', 'collections'] as const; const validatedModel = validateEnumValue(model, supportedModels, 'model', requestId, logWarn); // Validate pagination parameters let paginationOffset = 0; let paginationLimit: number | undefined = undefined; if (offset !== undefined) { paginationOffset = parseAndValidateNonNegativeInteger(offset, 'offset', requestId, logWarn); } if (limit !== undefined) { paginationLimit = parseAndValidatePositiveInteger(limit, 'limit', requestId, logWarn); if (paginationLimit > 1000) { logWarn('limit too large, maximum is 1000', { requestId, limit: paginationLimit }); throw ValidationErrorFactory.invalidParameter( 'limit', `${paginationLimit}`, 'Maximum allowed: 1000 items per page' ); } } logDebug( `Listing ${validatedModel} from Metabase ${paginationLimit ? `(paginated: offset=${paginationOffset}, limit=${paginationLimit})` : '(all items)'}` ); try { let optimizeFunction: (item: any) => any; let apiResponse: any; let dataSource: 'cache' | 'api'; switch (validatedModel) { case 'cards': { optimizeFunction = optimizeCardForList; const cardsResponse = await apiClient.getCardsList(); apiResponse = cardsResponse.data; dataSource = cardsResponse.source; break; } case 'dashboards': { optimizeFunction = optimizeDashboardForList; const dashboardsResponse = await apiClient.getDashboardsList(); apiResponse = dashboardsResponse.data; dataSource = dashboardsResponse.source; break; } case 'tables': { optimizeFunction = optimizeTableForList; const tablesResponse = await apiClient.getTablesList(); apiResponse = tablesResponse.data; dataSource = tablesResponse.source; break; } case 'databases': { optimizeFunction = optimizeDatabaseForList; const databasesResponse = await apiClient.getDatabasesList(); apiResponse = databasesResponse.data; dataSource = databasesResponse.source; break; } case 'collections': { optimizeFunction = optimizeCollectionForList; const collectionsResponse = await apiClient.getCollectionsList(); apiResponse = collectionsResponse.data; dataSource = collectionsResponse.source; break; } default: throw new Error(`Unsupported model: ${validatedModel}`); } logDebug( `Fetching ${validatedModel} from ${dataSource} (${dataSource === 'api' ? 'fresh data' : 'cached data'})` ); // Optimize each item for list view const optimizedItems = apiResponse.map(optimizeFunction); const totalItemsBeforePagination = optimizedItems.length; // Apply pagination if specified let paginatedItems = optimizedItems; let paginationMetadata: any = undefined; if (paginationLimit !== undefined) { const startIndex = paginationOffset; const endIndex = paginationOffset + paginationLimit; paginatedItems = optimizedItems.slice(startIndex, endIndex); // Add pagination metadata paginationMetadata = { total_items: totalItemsBeforePagination, offset: paginationOffset, limit: paginationLimit, current_page_size: paginatedItems.length, has_more: endIndex < totalItemsBeforePagination, next_offset: endIndex < totalItemsBeforePagination ? endIndex : undefined, }; } const totalItems = paginatedItems.length; logDebug( `Successfully fetched ${totalItemsBeforePagination} ${validatedModel}${paginationLimit ? ` (returning ${totalItems} paginated items)` : ''}` ); // Create response object const response: any = { model: validatedModel, total_items: totalItems, source: dataSource, results: paginatedItems, }; // Add pagination metadata if pagination was used if (paginationMetadata) { response.pagination = paginationMetadata; } // Add usage guidance if (paginationLimit !== undefined) { response.usage_guidance = 'This list provides a paginated overview of available items. Use offset and limit parameters for pagination when dealing with large datasets that exceed token limits. Use retrieve() with specific model types and IDs to get detailed information for further operations like execute_query.'; } else { response.usage_guidance = 'This list provides an overview of available items. Use retrieve() with specific model types and IDs to get detailed information for further operations like execute_query. For large datasets exceeding token limits, use offset and limit parameters for pagination.'; } // Add model-specific recommendation switch (validatedModel) { case 'cards': response.recommendation = 'Use retrieve(model="card", ids=[...]) to get SQL queries and execute them with execute_query()'; break; case 'dashboards': response.recommendation = 'Use retrieve(model="dashboard", ids=[...]) to get dashboard details and card information'; break; case 'tables': response.recommendation = 'Use retrieve(model="table", ids=[...]) to get detailed schema information for query construction'; break; case 'databases': response.recommendation = 'Use retrieve(model="database", ids=[...]) to get connection details and available tables'; break; case 'collections': response.recommendation = 'Use retrieve(model="collection", ids=[...]) to get organizational structure and content management details'; break; } logInfo(`Successfully listed ${totalItems} ${validatedModel}`); return { content: [ { type: 'text', text: formatJson(response), }, ], }; } catch (error: any) { throw handleApiError( error, { operation: `List ${validatedModel}`, resourceType: validatedModel }, logError ); } }
- src/server.ts:295-329 (schema)The tool schema definition for 'list', including name, description, annotations, and input schema, returned in response to list tools requests.name: 'list', description: 'Fetch all records for a single Metabase resource type with highly optimized responses for overview purposes. Retrieves complete lists of cards, dashboards, tables, databases, or collections. Returns only essential identifier fields for efficient browsing and includes intelligent caching for performance. Supports pagination for large datasets exceeding token limits.', annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, inputSchema: { type: 'object', properties: { model: { type: 'string', enum: ['cards', 'dashboards', 'tables', 'databases', 'collections'], description: 'Model type to list ALL records for. Supported models: cards (all questions/queries), dashboards (all dashboards), tables (all database tables), databases (all connected databases), collections (all folders/collections). Only one model type allowed per request for optimal performance.', }, offset: { type: 'number', description: 'Starting offset for pagination. Use with limit for paginating through large datasets that exceed token limits.', minimum: 0, }, limit: { type: 'number', description: 'Maximum number of items to return per page. Maximum 1000 items per page. Use with offset for pagination.', minimum: 1, maximum: 1000, }, }, required: ['model'], }, },
- src/server.ts:518-528 (registration)The dispatch registration in the CallToolRequest handler that routes 'list' tool calls to the handleList function.return safeCall(() => handleList( request, requestId, this.apiClient, this.logDebug.bind(this), this.logInfo.bind(this), this.logWarn.bind(this), this.logError.bind(this) ) );
- Supporting optimizer functions that trim API responses to essential fields only for efficient list views of cards, dashboards, tables, databases, and collections.import { ListCard, ListDashboard, ListTable, ListDatabase, ListCollection } from './types.js'; /** * Optimize card response for list view - only essential identifier fields */ export function optimizeCardForList(card: any): ListCard { const optimized: ListCard = { id: card.id, name: card.name, database_id: card.database_id, }; if (card.description) { optimized.description = card.description; } if (card.collection_id !== null && card.collection_id !== undefined) { optimized.collection_id = card.collection_id; } if (card.archived !== undefined) { optimized.archived = card.archived; } if (card.created_at) { optimized.created_at = card.created_at; } if (card.updated_at) { optimized.updated_at = card.updated_at; } return optimized; } /** * Optimize dashboard response for list view - only essential identifier fields */ export function optimizeDashboardForList(dashboard: any): ListDashboard { const optimized: ListDashboard = { id: dashboard.id, name: dashboard.name, }; if (dashboard.description) { optimized.description = dashboard.description; } if (dashboard.collection_id !== null && dashboard.collection_id !== undefined) { optimized.collection_id = dashboard.collection_id; } if (dashboard.archived !== undefined) { optimized.archived = dashboard.archived; } if (dashboard.created_at) { optimized.created_at = dashboard.created_at; } if (dashboard.updated_at) { optimized.updated_at = dashboard.updated_at; } return optimized; } /** * Optimize table response for list view - only essential identifier fields */ export function optimizeTableForList(table: any): ListTable { const optimized: ListTable = { id: table.id, name: table.name, display_name: table.display_name, db_id: table.db_id, active: table.active, }; if (table.schema) { optimized.schema = table.schema; } if (table.entity_type) { optimized.entity_type = table.entity_type; } return optimized; } /** * Optimize database response for list view - only essential identifier fields */ export function optimizeDatabaseForList(database: any): ListDatabase { const optimized: ListDatabase = { id: database.id, name: database.name, engine: database.engine, }; if (database.description) { optimized.description = database.description; } if (database.is_sample !== undefined) { optimized.is_sample = database.is_sample; } if (database.created_at) { optimized.created_at = database.created_at; } if (database.updated_at) { optimized.updated_at = database.updated_at; } return optimized; } /** * Optimize collection response for list view - only essential identifier fields */ export function optimizeCollectionForList(collection: any): ListCollection { const optimized: ListCollection = { id: collection.id, name: collection.name, slug: collection.slug, archived: collection.archived, is_personal: collection.is_personal, }; if (collection.description) { optimized.description = collection.description; } if (collection.location) { optimized.location = collection.location; } if (collection.created_at) { optimized.created_at = collection.created_at; } return optimized; }
- src/handlers/list/types.ts:1-59 (schema)TypeScript interfaces defining the structure of optimized list response items for input/output validation.// Supported model types for the list command export type SupportedListModel = 'cards' | 'dashboards' | 'tables' | 'databases' | 'collections'; // Highly optimized list response interfaces - only essential identifier fields export interface ListCard { id: number; name: string; description?: string; database_id: number; collection_id?: number; archived?: boolean; created_at?: string; updated_at?: string; } export interface ListDashboard { id: number; name: string; description?: string; collection_id?: number; archived?: boolean; created_at?: string; updated_at?: string; } export interface ListTable { id: number; name: string; display_name: string; db_id: number; schema?: string; entity_type?: string; active: boolean; } export interface ListDatabase { id: number; name: string; engine: string; description?: string; is_sample?: boolean; created_at?: string; updated_at?: string; } export interface ListCollection { id: number; name: string; description?: string; slug: string; archived: boolean; location?: string; is_personal: boolean; created_at?: string; } // Union type for all list response types export type ListResponse = ListCard | ListDashboard | ListTable | ListDatabase | ListCollection;