Skip to main content
Glama
aliyun

Alibaba Cloud Supabase MCP Server

Official
by aliyun
aliyun-tools.ts20.7 kB
import type { SupabasePlatform } from '../platform/types.js'; import { type Tool, tool } from '@supabase/mcp-utils'; import { z } from 'zod'; import { exec } from 'child_process'; export type AliyunToolsOptions = { platform: SupabasePlatform; }; /** * 安全地转义一个字符串,以便在 shell 命令中作为单个参数使用。 * @param {string} arg 要转义的参数。 * @returns {string} 转义后的参数,已用单引号包裹。 */ const escapeShellArg = (arg: string): string => { // 1. 将字符串中的所有单引号 ' 替换为 '\'' // 这表示:结束当前的单引号字符串,插入一个转义的单引号,然后开始一个新的单引号字符串。 // 2. 用单引号将整个结果包裹起来。 return `'${arg.replace(/'/g, "'\\''")}'`; }; export async function getAliyunTools({ platform }: AliyunToolsOptions): Promise<Record<string, Tool>> { return { list_aliyun_supabase_projects: tool({ description: 'Lists all Supabase projects deployed on the Aliyun platform. Use this to retrieve a list of existing projects with their basic information. If no projects are found in the default region (cn-hangzhou), try other regions obtained from the describe_regions tool.', parameters: z.object({ region_id: z.string().optional().describe('Region ID (e.g., cn-hangzhou, cn-shanghai, cn-beijing, etc.)'), next_token: z.string().optional().describe('Next token for pagination'), max_results: z.number().optional().describe('Maximum number of results to return') }), execute: async (options) => { try { const result = await platform.listAliyunSupabaseProjects(options); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } catch (error) { // 类型检查确保 error 是 Error 实例 const message = error instanceof Error ? error.message : 'Unknown error occurred'; return { content: [{ type: 'text', text: `Error: ${message}` }] }; } } }), get_supabase_project: tool({ description: 'Gets details for a specific Supabase project on Aliyun platform.', parameters: z.object({ project_id: z.string().describe('The ID of the Supabase project.'), region_id: z.string().optional().describe('Region ID'), }), execute: async (options) => { try { // 这里的 options 将是 { project_id: '...' } const result = await platform.getAliyunSupabaseProject(options); // 返回 JSON 格式的结果,让 AI 自己去解析和总结 return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error occurred'; return { content: [{ type: 'text', text: `Error: ${message}` }] }; } } }), // get_supabase_project_dashboard_account: tool({ // description: 'Gets the Supabase project dashboard account information.', // parameters: z.object({ // project_id: z.string().describe('The Supabase instance ID.'), // region_id: z.string().optional().describe('Region ID'), // }), // execute: async (options) => { // try { // const result = await platform.getAliyunSupabaseProjectDashboardAccount(options); // return { // content: [{ // type: 'text', // text: JSON.stringify(result, null, 2) // }] // }; // } catch (error) { // throw new Error(`Failed to get dashboard account: ${error instanceof Error ? error.message : 'Unknown error'}`); // } // }, // }), get_supabase_project_api_keys: tool({ description: 'Gets the Supabase project API keys including anon key and serviceRoleKey.', parameters: z.object({ project_id: z.string().describe('The Supabase instance ID.'), region_id: z.string().optional().describe('The region ID where the instance is located.'), }), execute: async (options) => { try { const result = await platform.getAliyunSupabaseProjectApiKeys(options); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error occurred'; return { content: [{ type: 'text', text: `Error: ${message}` }] }; } } }), modify_supabase_project_security_ip_list: tool({ description: 'Modify the IP whitelist for a Supabase project. You need to add the client IP address or IP address range to the whitelist before using the Supabase instance.', parameters: z.object({ project_id: z.string().describe('The Supabase instance ID.'), region_id: z.string().optional().describe('Region ID. You can call the DescribeRegions API to view available region IDs.'), security_ip_list: z.string().describe('Comma-separated list of IP addresses or CIDR blocks to add to the whitelist. Up to 1000 entries. Format: 10.23.12.24 (IP) or 10.23.12.24/24 (CIDR)'), }), execute: async (options) => { try { const result = await platform.modifyAliyunSupabaseProjectSecurityIps({ project_id: options.project_id, region_id: options.region_id, security_ip_list: options.security_ip_list.split(',').map(ip => ip.trim()) }); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error occurred'; return { content: [{ type: 'text', text: `Error: ${message}` }] }; } } }), reset_supabase_project_password: tool({ description: 'Reset the database password for a Supabase project.', parameters: z.object({ project_id: z.string().describe('The Supabase instance ID.'), region_id: z.string().optional().describe('Instance region ID.'), account_password: z.string().describe('Database account password. Must contain at least three of the following: uppercase letters, lowercase letters, numbers, and special characters. Special characters include: !@#$%^&*()_+-=. Password length must be between 8 and 32 characters.'), }), execute: async (options) => { try { const result = await platform.resetAliyunSupabaseProjectPassword(options); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error occurred'; return { content: [{ type: 'text', text: `Error: ${message}` }] }; } } }), create_supabase_project: tool({ description: 'Create a new Supabase project on Aliyun platform.', parameters: z.object({ project_name: z.string().describe('Project name. Must be 1-128 characters long. Can only contain English letters, numbers, hyphens (-) and underscores (_). Must start with an English letter or underscore (_).'), zone_id: z.string().describe('Zone ID. You can call the DescribeRegions API to view available zone IDs.'), account_password: z.string().describe('Initial account password. Must contain at least three of the following: uppercase letters, lowercase letters, numbers, and special characters. Special characters include: !@#$%^&*()_+-=. Password length must be between 8 and 32 characters.'), security_ip_list: z.string().describe('IP whitelist. 127.0.0.1 means禁止任何外部 IP 访问, you can modify the IP whitelist after the instance is created by calling the ModifySecurityIps API.'), vpc_id: z.string().describe('VPC ID. You can call the DescribeRdsVpcs API to view available VPC IDs. This parameter is required.'), v_switch_id: z.string().describe('vSwitch ID. vSwitchId is required. The zone where the vSwitch is located must be consistent with ZoneId.'), project_spec: z.string().describe('Supabase instance specification, default is 1C1G.'), region_id: z.string().optional().describe('Region ID. You can call the DescribeRegions API to view available region IDs.'), storage_size: z.number().optional().describe('Storage space size in GB, default is 1GB.'), disk_performance_level: z.enum(['PL0', 'PL1']).optional().describe('Cloud disk PL level, default is PL0.'), client_token: z.string().optional().describe('Idempotency check. For more information, see How to Ensure Idempotency.'), }), execute: async (options) => { try { const result = await platform.createAliyunSupabaseProject(options); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error occurred'; return { content: [{ type: 'text', text: `Error: ${message}` }] }; } } }), delete_supabase_project: tool({ description: 'Delete a Supabase project on Aliyun platform.', parameters: z.object({ project_id: z.string().describe('Supabase project ID. You can log in to the console Supabase page to get the workspace ID.'), region_id: z.string().optional().describe('The region ID where the instance is located.'), }), execute: async (options) => { try { const result = await platform.deleteAliyunSupabaseProject(options); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error occurred'; return { content: [{ type: 'text', text: `Error: ${message}` }] }; } } }), describe_regions: tool({ description: 'Describe available regions and zones for Aliyun Supabase projects.', parameters: z.object({}), execute: async () => { try { const result = await platform.describeRegions(); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error occurred'; return { content: [{ type: 'text', text: `Error: ${message}` }] }; } } }), describe_rds_vpcs: tool({ description: 'Describe available VPCs in Aliyun for Supabase project deployment', parameters: z.object({ region_id: z.string().optional().describe('Region ID. You can call the DescribeRegions API to view available region IDs.'), }), execute: async (options) => { try { const result = await platform.describeRdsVpcs(options); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error occurred'; return { content: [{ type: 'text', text: `Error: ${message}` }] }; } } }), describe_rds_vswitches: tool({ description: 'Describe available vSwitches in Aliyun for Supabase project deployment', parameters: z.object({ region_id: z.string().optional().describe('Region ID. You can call the DescribeRegions API to view available region IDs.'), zone_id: z.string().describe('Zone ID. Must be consistent with the zone where the Supabase instance will be deployed.'), vpc_id: z.string().describe('VPC ID. The VPC where the vSwitches are located.'), }), execute: async (options) => { try { const result = await platform.describeRdsVSwitches(options); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error occurred'; return { content: [{ type: 'text', text: `Error: ${message}` }] }; } } }), execute_sql: tool({ description: 'Executes custom SQL queries on a Supabase project database. First tries to use TypeScript interface, falls back to curl command if needed. Requires PublicConnectUrl and serviceRoleKey.', parameters: z.object({ url: z.string().describe('PublicConnectUrl for the Supabase project'), api_key: z.string().describe('serviceRoleKey for authentication'), sql: z.string().describe('SQL query to execute') }), execute: async ({ url, api_key, sql }) => { try { // 首先尝试使用 TypeScript 接口 (fetch) try { console.error('Debug - Attempting to execute SQL via TypeScript interface'); const result = await platform.executeSqlViaUrl({ url, api_key, sql }); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } catch (tsError) { console.error(`Debug - TypeScript interface failed: ${tsError instanceof Error ? tsError.message : 'Unknown error'}`); console.error('Debug - Falling back to curl command'); // 如果 TypeScript 接口失败,回退到 curl 命令 // 1. 准备和格式化参数 let requestUrl = url; if (!requestUrl.startsWith('http://') && !requestUrl.startsWith('https://')) { requestUrl = "http://" + requestUrl; } requestUrl = requestUrl + "/pg/query"; const jsonData = JSON.stringify({ query: sql }); // 2. 安全地构建 curl 命令 // 使用 escapeShellArg 对每个动态部分进行转义,防止命令注入 const command = [ 'curl', '-X POST', escapeShellArg(requestUrl), '-H', escapeShellArg(`apikey: ${api_key}`), '-H', escapeShellArg('Content-Type: application/json'), '-d', escapeShellArg(jsonData) ].join(' '); console.error(`Debug - Executing curl command: ${command}`); // 3. 执行命令并等待结果 const result = await new Promise((resolve, reject) => { exec(command, (error, stdout, stderr) => { if (error) { // 如果命令执行失败(例如 curl 不存在,或返回非 0 退出码) console.error(`Debug - exec error: ${error.message}`); console.error(`Debug - stderr: ${stderr}`); reject(new Error(`Command failed: ${stderr || error.message}`)); return; } if (stderr) { // curl 可能会将进度信息等输出到 stderr,但我们仍然可以继续 console.warn(`Debug - stderr output: ${stderr}`); } try { // 尝试解析 stdout 输出的 JSON const parsedOutput = JSON.parse(stdout); resolve(parsedOutput); } catch (parseError) { // 如果 stdout 不是有效的 JSON const parseErrorMessage = parseError instanceof Error ? parseError.message : String(parseError); console.error(`Debug - JSON parse error: ${parseErrorMessage}`); console.error(`Debug - stdout received: ${stdout}`); reject(new Error(`Failed to parse curl output as JSON: ${stdout}`)); } }); }); // 4. 返回成功的结果 return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error occurred'; console.error(`Debug - Error: ${message}`); return { content: [{ type: 'text', text: `Error: ${message}` }] }; } } }), list_table: tool({ description: 'Lists all tables in the public schema of a Supabase project database. First tries to use TypeScript interface, falls back to curl command if needed. Requires the project\'s PublicConnectUrl as url and serviceRoleKey as api_key obtained from other tools.', parameters: z.object({ url: z.string().describe('PublicConnectUrl for the Supabase project'), api_key: z.string().describe('serviceRoleKey for authentication'), }), execute: async ({ url, api_key }) => { try { // 首先尝试使用 TypeScript 接口 (fetch) try { console.error('Debug - Attempting to list tables via TypeScript interface'); const result = await platform.listTablesViaUrl({ url, api_key }); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } catch (tsError) { console.error(`Debug - TypeScript interface failed: ${tsError instanceof Error ? tsError.message : 'Unknown error'}`); console.error('Debug - Falling back to curl command'); // 如果 TypeScript 接口失败,回退到 curl 命令 // 1. 准备和格式化参数 let requestUrl = url; if (!requestUrl.startsWith('http://') && !requestUrl.startsWith('https://')) { requestUrl = "http://" + requestUrl; } requestUrl = requestUrl + "/pg/query"; const sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'"; const jsonData = JSON.stringify({ query: sql }); // 2. 安全地构建 curl 命令 const command = [ 'curl', '-X POST', escapeShellArg(requestUrl), '-H', escapeShellArg(`apikey: ${api_key}`), '-H', escapeShellArg('Content-Type: application/json'), '-d', escapeShellArg(jsonData) ].join(' '); console.error(`Debug list_table - Executing curl command: ${command}`); // 3. 执行命令并等待结果 const result = await new Promise((resolve, reject) => { exec(command, (error, stdout, stderr) => { if (error) { console.error(`Debug - exec error: ${error.message}`); console.error(`Debug - stderr: ${stderr}`); reject(new Error(`Command failed: ${stderr || error.message}`)); return; } if (stderr) { console.warn(`Debug - stderr output: ${stderr}`); } try { const parsedOutput = JSON.parse(stdout); resolve(parsedOutput); } catch (parseError) { const parseErrorMessage = parseError instanceof Error ? parseError.message : String(parseError); console.error(`Debug - JSON parse error: ${parseErrorMessage}`); console.error(`Debug - stdout received: ${stdout}`); reject(new Error(`Failed to parse curl output as JSON: ${stdout}`)); } }); }); // 4. 返回成功的结果 return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } } catch (error) { const message = error instanceof Error ? error.message : 'Unknown error occurred'; console.error(`Debug - Error: ${message}`); return { content: [{ type: 'text', text: `Error: ${message}` }] }; } } }), }; }

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/aliyun/alibabacloud-supabase-mcp-server'

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