Skip to main content
Glama
index.ts6.54 kB
import { CallToolRequest } from '@modelcontextprotocol/sdk/types.js'; import { MetabaseApiClient } from '../../api.js'; import { ErrorCode, McpError } from '../../types/core.js'; import { validateCardParameters, validatePositiveInteger, validateRowLimit, } from '../../utils/index.js'; import { executeSqlQuery } from './executeQuery.js'; import { executeCard } from './executeCard.js'; import { ExecuteRequest, SqlExecutionParams, CardExecutionParams, ExecutionResponse, } from './types.js'; export async function handleExecute( 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, error: unknown) => void ): Promise<ExecutionResponse> { const args = request.params?.arguments as ExecuteRequest; const databaseId = args?.database_id; const query = args?.query; const cardId = args?.card_id; const nativeParameters = Array.isArray(args?.native_parameters) ? args.native_parameters : []; const cardParameters = Array.isArray(args?.card_parameters) ? args.card_parameters : []; const rowLimitArg = args?.row_limit; const rowLimit = typeof rowLimitArg === 'number' ? rowLimitArg : 100; // First validate that parameter types are correct if (cardId !== undefined && typeof cardId !== 'number') { logWarn('Invalid card_id parameter - must be a number', { requestId }); throw new McpError(ErrorCode.InvalidParams, 'card_id parameter must be a number'); } if (databaseId !== undefined && typeof databaseId !== 'number') { logWarn('Invalid database_id parameter - must be a number', { requestId }); throw new McpError(ErrorCode.InvalidParams, 'database_id parameter must be a number'); } // Validate that either query+database_id or card_id is provided if (!cardId && !databaseId) { logWarn('Missing required parameters: either card_id or database_id must be provided', { requestId, }); throw new McpError( ErrorCode.InvalidParams, 'Either card_id or database_id parameter is required' ); } if (cardId && databaseId) { logWarn('Both card_id and database_id provided - only one is allowed', { requestId }); throw new McpError( ErrorCode.InvalidParams, 'Cannot specify both card_id and database_id - choose one execution method' ); } // Validate positive integer parameters if (cardId !== undefined) { validatePositiveInteger(cardId, 'card_id', requestId, logWarn); } if (databaseId !== undefined) { validatePositiveInteger(databaseId, 'database_id', requestId, logWarn); } validateRowLimit(rowLimit, 'row_limit', requestId, logWarn); // Strict parameter validation for card execution mode if (cardId) { // For card execution, only card_id, card_parameters, and row_limit are allowed if (query || databaseId || (nativeParameters && nativeParameters.length > 0)) { logWarn('Invalid parameters for card execution mode', { requestId, invalidParams: { query: query ? 'provided' : 'not provided', database_id: databaseId ? 'provided' : 'not provided', native_parameters: nativeParameters?.length > 0 ? 'provided' : 'not provided', }, }); throw new McpError( ErrorCode.InvalidParams, 'Card execution mode only allows card_id, card_parameters, and row_limit parameters' ); } } // Strict parameter validation for SQL execution mode if (databaseId) { // For SQL execution, only database_id, query, native_parameters, and row_limit are allowed if (cardId || (cardParameters && cardParameters.length > 0)) { logWarn('Invalid parameters for SQL execution mode', { requestId, invalidParams: { card_id: cardId ? 'provided' : 'not provided', card_parameters: cardParameters?.length > 0 ? 'provided' : 'not provided', }, }); throw new McpError( ErrorCode.InvalidParams, 'SQL execution mode only allows database_id, query, native_parameters, and row_limit parameters' ); } } // If executing a card if (cardId) { validatePositiveInteger(cardId, 'card_id', requestId, logWarn); // Validate row limit for cards if (rowLimit < 1 || rowLimit > 500) { logWarn(`Invalid row_limit parameter: ${rowLimit}. Must be between 1 and 500.`, { requestId, }); throw new McpError( ErrorCode.InvalidParams, 'Row limit must be between 1 and 500. For larger datasets, use the export tool instead.' ); } // Validate card parameters format if provided if (cardParameters.length > 0) { try { validateCardParameters(cardParameters, requestId, logWarn); } catch (error) { logWarn(`Card parameter validation failed for card ${cardId}`, { error, requestId }); throw new McpError( ErrorCode.InvalidParams, `Invalid card parameters format. If parameter issues persist, consider using execute_query with the card's underlying SQL query instead, which provides more reliable parameter handling. Original error: ${error instanceof Error ? error.message : String(error)}` ); } } const cardParams: CardExecutionParams = { cardId, cardParameters, rowLimit, }; return await executeCard( cardParams, requestId, apiClient, logDebug, logInfo, logWarn, logError ); } // If executing a SQL query if (!query || typeof query !== 'string') { logWarn('Missing or invalid query parameter in execute request', { requestId }); throw new McpError( ErrorCode.InvalidParams, 'SQL query parameter is required and must be a string' ); } // Validate row limit for SQL queries if (rowLimit < 1 || rowLimit > 500) { logWarn(`Invalid row_limit parameter: ${rowLimit}. Must be between 1 and 500.`, { requestId }); throw new McpError( ErrorCode.InvalidParams, 'Row limit must be between 1 and 500. For larger datasets, use the export tool instead.' ); } const sqlParams: SqlExecutionParams = { databaseId: databaseId as number, query, nativeParameters, rowLimit, }; return await executeSqlQuery( sqlParams, requestId, apiClient, logDebug, logInfo, logWarn, logError ); }

Latest Blog Posts

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/jerichosequitin/Metabase'

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