/** * list_edge_function_logs - Lists execution logs for edge functions. * * Queries the function_edge_logs table if available in the Supabase instance. * This table is automatically created by Supabase for edge function logging. */ import { z } from 'zod'; import { handleSqlResponse, executeSqlWithFallback } from './utils.js'; import type { ToolContext, ToolPrivilegeLevel } from './types.js'; const EdgeFunctionLogSchema = z.object({ execution_id: z.string().nullable(), function_id: z.string(), status_code: z.number().nullable(), request_start_time: z.string(), request_duration_ms: z.number().nullable(), error_message: z.string().nullable(), request_path: z.string().nullable(), request_method: z.string().nullable(), }); const ListEdgeFunctionLogsOutputSchema = z.array(EdgeFunctionLogSchema); const ListEdgeFunctionLogsInputSchema = z.object({ function_id: z.string().optional().describe('Filter by function ID/slug.'), status_code: z.number().optional().describe('Filter by HTTP status code.'), errors_only: z.boolean().optional().describe('Only show logs with errors (status >= 400).'), limit: z.number().optional().default(100).describe('Maximum number of log entries to return.'), }); type ListEdgeFunctionLogsInput = z.infer; const mcpInputSchema = { type: 'object', properties: { function_id: { type: 'string', description: 'Filter by function ID/slug.', }, status_code: { type: 'number', description: 'Filter by HTTP status code.', }, errors_only: { type: 'boolean', description: 'Only show logs with errors (status >= 400).', }, limit: { type: 'number', description: 'Maximum number of log entries to return.', default: 100, }, }, required: [], }; // Pattern for function IDs (UUIDs or slugs) const functionIdPattern = /^[a-zA-Z0-9_\-]+$/; export const listEdgeFunctionLogsTool = { name: 'list_edge_function_logs', description: 'Lists execution logs for edge functions from the function_edge_logs table.', privilegeLevel: 'regular' as ToolPrivilegeLevel, inputSchema: ListEdgeFunctionLogsInputSchema, mcpInputSchema: mcpInputSchema, outputSchema: ListEdgeFunctionLogsOutputSchema, execute: async (input: ListEdgeFunctionLogsInput, context: ToolContext) => { const client = context.selfhostedClient; const { function_id, status_code, errors_only, limit = 100 } = input; // Validate function_id if provided if (function_id && !functionIdPattern.test(function_id)) { throw new Error(`Invalid function ID: ${function_id}. Use only alphanumeric, underscore, and hyphen characters.`); } // Check if function_edge_logs table exists const checkTableSql = ` SELECT EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_name = 'function_edge_logs' ) as exists; `; const tableCheck = await executeSqlWithFallback(client, checkTableSql, true); if (!Array.isArray(tableCheck) || tableCheck.length === 0) { throw new Error('Failed to check for function_edge_logs table.'); } if (!tableCheck[0].exists) { throw new Error( 'Edge function logs table (function_edge_logs) not found. ' + 'This table is automatically created by Supabase when edge functions are invoked. ' + 'Ensure edge functions have been executed at least once.' ); } // Build query with filters const conditions: string[] = []; if (function_id) { conditions.push(`function_id = '${function_id}'`); } if (status_code !== undefined) { // status_code is a number from Zod validation, safe to use directly conditions.push(`status_code = ${status_code}`); } if (errors_only) { conditions.push('status_code >= 400'); } const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''; // Ensure limit is within bounds const safeLimit = Math.min(Math.max(1, limit), 1000); const logsSql = ` SELECT execution_id::text, function_id, status_code, request_start_time::text, EXTRACT(EPOCH FROM (request_end_time - request_start_time)) * 1000 as request_duration_ms, error_message, request_path, request_method FROM function_edge_logs ${whereClause} ORDER BY request_start_time DESC LIMIT ${safeLimit} `; const result = await executeSqlWithFallback(client, logsSql, true); return handleSqlResponse(result, ListEdgeFunctionLogsOutputSchema); }, };