@aliyun-rds/supabase-mcp-server 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +87 -21
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -813,6 +813,13 @@ var SelfhostedSupabaseClient = class _SelfhostedSupabaseClient {
813
813
  isRpcAvailable() {
814
814
  return this.rpcFunctionExists;
815
815
  }
816
+ /**
817
+ * Sets the RPC function availability status.
818
+ * Called after successfully installing the execute_sql function.
819
+ */
820
+ setRpcAvailable(available) {
821
+ this.rpcFunctionExists = available;
822
+ }
816
823
  /**
817
824
  * Gets the authentication mode this client was created with.
818
825
  */
@@ -865,6 +872,9 @@ import { z } from "zod";
865
872
  import { exec } from "node:child_process";
866
873
  import { promisify } from "node:util";
867
874
  var execAsync = promisify(exec);
875
+ function isSqlErrorResponse(result) {
876
+ return result.error !== void 0;
877
+ }
868
878
  function handleSqlResponse(result, schema) {
869
879
  if ("error" in result) {
870
880
  throw new Error(`SQL Error (${result.error.code}): ${result.error.message}`);
@@ -894,10 +904,10 @@ async function runExternalCommand(command) {
894
904
  }
895
905
  async function executeSqlWithFallback(client, sql, readOnly = true) {
896
906
  if (client.isPgAvailable()) {
897
- console.info("Using direct database connection (bypassing JWT)...");
907
+ console.warn("Using direct database connection (bypassing JWT)...");
898
908
  return await client.executeSqlWithPg(sql);
899
909
  }
900
- console.info("Falling back to RPC method...");
910
+ console.warn("Falling back to RPC method...");
901
911
  return await client.executeSqlViaRpc(sql, readOnly);
902
912
  }
903
913
 
@@ -1277,7 +1287,22 @@ var getDatabaseStatsTool = {
1277
1287
  stats_reset::text
1278
1288
  FROM pg_stat_database
1279
1289
  `;
1280
- const getBgWriterStatsSql = `
1290
+ const getBgWriterStatsSqlPg17 = `
1291
+ SELECT
1292
+ (SELECT num_timed::text FROM pg_stat_checkpointer) as checkpoints_timed,
1293
+ (SELECT num_requested::text FROM pg_stat_checkpointer) as checkpoints_req,
1294
+ (SELECT write_time FROM pg_stat_checkpointer) as checkpoint_write_time,
1295
+ (SELECT sync_time FROM pg_stat_checkpointer) as checkpoint_sync_time,
1296
+ (SELECT buffers_written::text FROM pg_stat_checkpointer) as buffers_checkpoint,
1297
+ buffers_clean::text,
1298
+ maxwritten_clean::text,
1299
+ NULL::text as buffers_backend,
1300
+ NULL::text as buffers_backend_fsync,
1301
+ buffers_alloc::text,
1302
+ stats_reset::text
1303
+ FROM pg_stat_bgwriter
1304
+ `;
1305
+ const getBgWriterStatsSqlPg16 = `
1281
1306
  SELECT
1282
1307
  checkpoints_timed::text,
1283
1308
  checkpoints_req::text,
@@ -1292,10 +1317,12 @@ var getDatabaseStatsTool = {
1292
1317
  stats_reset::text
1293
1318
  FROM pg_stat_bgwriter
1294
1319
  `;
1295
- const [dbStatsResult, bgWriterStatsResult] = await Promise.all([
1296
- executeSqlWithFallback(client, getDbStatsSql, true),
1297
- executeSqlWithFallback(client, getBgWriterStatsSql, true)
1298
- ]);
1320
+ const dbStatsResult = await executeSqlWithFallback(client, getDbStatsSql, true);
1321
+ let bgWriterStatsResult = await executeSqlWithFallback(client, getBgWriterStatsSqlPg17, true);
1322
+ if (isSqlErrorResponse(bgWriterStatsResult)) {
1323
+ console.error("PostgreSQL 17+ query failed, trying PostgreSQL 16 query...");
1324
+ bgWriterStatsResult = await executeSqlWithFallback(client, getBgWriterStatsSqlPg16, true);
1325
+ }
1299
1326
  const dbStats = handleSqlResponse(dbStatsResult, GetDbStatsOutputSchema.shape.database_stats);
1300
1327
  const bgWriterStats = handleSqlResponse(bgWriterStatsResult, GetDbStatsOutputSchema.shape.bgwriter_stats);
1301
1328
  return {
@@ -2315,6 +2342,7 @@ async function execute(input, context) {
2315
2342
  }
2316
2343
  context.log("execute_sql function installed successfully!", "info");
2317
2344
  context.log("You may need to wait a few seconds for PostgREST to reload the schema.", "info");
2345
+ client.setRpcAvailable(true);
2318
2346
  return {
2319
2347
  success: true,
2320
2348
  message: "execute_sql function installed successfully. You can now use tools that require SQL execution (list_tables, execute_sql, etc.)."
@@ -2663,32 +2691,67 @@ function wrapAllRagAgentTools(ragClient) {
2663
2691
  import RdsAiPkg from "@alicloud/rdsai20250507/dist/client.js";
2664
2692
  import * as $RdsAi from "@alicloud/rdsai20250507";
2665
2693
  var RdsAiClient = RdsAiPkg.default || RdsAiPkg;
2694
+ var RDSAI_DEFAULT_ENDPOINT = "rdsai.aliyuncs.com";
2695
+ var RDSAI_CENTER_UNIT_REGIONS = /* @__PURE__ */ new Set([
2696
+ "cn-beijing",
2697
+ "cn-wulanchabu",
2698
+ "cn-hangzhou",
2699
+ "cn-shanghai",
2700
+ "cn-shenzhen",
2701
+ "cn-guangzhou"
2702
+ ]);
2703
+ function resolveRdsAiEndpoint(regionId, explicitEndpoint) {
2704
+ if (explicitEndpoint) {
2705
+ return explicitEndpoint;
2706
+ }
2707
+ if (!regionId || RDSAI_CENTER_UNIT_REGIONS.has(regionId)) {
2708
+ return RDSAI_DEFAULT_ENDPOINT;
2709
+ }
2710
+ return `rdsai.${regionId}.aliyuncs.com`;
2711
+ }
2666
2712
  var AliyunRdsAiClient = class {
2667
- client;
2668
- // RdsAi Client instance
2713
+ clientCache;
2669
2714
  config;
2670
2715
  constructor(config) {
2671
2716
  this.config = config;
2717
+ this.clientCache = /* @__PURE__ */ new Map();
2718
+ const defaultEndpoint = resolveRdsAiEndpoint(config.regionId, config.endpoint);
2719
+ this.clientCache.set(defaultEndpoint, this.createClient(defaultEndpoint, config.regionId));
2720
+ }
2721
+ createClient(endpoint, regionId) {
2672
2722
  const openApiConfig = {
2673
- accessKeyId: config.accessKeyId,
2674
- accessKeySecret: config.accessKeySecret,
2675
- endpoint: config.endpoint || "rdsai.aliyuncs.com"
2723
+ accessKeyId: this.config.accessKeyId,
2724
+ accessKeySecret: this.config.accessKeySecret,
2725
+ endpoint,
2726
+ regionId
2676
2727
  };
2677
- this.client = new RdsAiClient(openApiConfig);
2728
+ return new RdsAiClient(openApiConfig);
2729
+ }
2730
+ getClientForRegion(regionId) {
2731
+ const effectiveRegionId = regionId || this.config.regionId;
2732
+ const endpoint = resolveRdsAiEndpoint(effectiveRegionId, this.config.endpoint);
2733
+ const cached = this.clientCache.get(endpoint);
2734
+ if (cached) {
2735
+ return cached;
2736
+ }
2737
+ const client = this.createClient(endpoint, effectiveRegionId);
2738
+ this.clientCache.set(endpoint, client);
2739
+ return client;
2678
2740
  }
2679
2741
  /**
2680
2742
  * List all Supabase instances
2681
2743
  */
2682
2744
  async listSupabaseInstances(options) {
2745
+ const regionId = options?.regionId || this.config.regionId;
2683
2746
  const request = new $RdsAi.DescribeAppInstancesRequest({
2684
2747
  appType: "supabase",
2685
- regionId: options?.regionId || this.config.regionId,
2748
+ regionId,
2686
2749
  DBInstanceName: options?.dbInstanceName,
2687
2750
  pageSize: options?.pageSize || 50,
2688
2751
  pageNumber: options?.pageNumber || 1
2689
2752
  });
2690
2753
  try {
2691
- const response = await this.client.describeAppInstances(request);
2754
+ const response = await this.getClientForRegion(regionId).describeAppInstances(request);
2692
2755
  if (!response.body) {
2693
2756
  throw new Error("Empty response from DescribeAppInstances API");
2694
2757
  }
@@ -2714,19 +2777,21 @@ var AliyunRdsAiClient = class {
2714
2777
  instances
2715
2778
  };
2716
2779
  } catch (error) {
2717
- console.error("Error calling DescribeAppInstances:", error);
2780
+ console.error("Error calling DescribeAppInstances:", JSON.stringify(error, null, 2));
2781
+ console.error("Error code:", error?.code);
2782
+ console.error("Error data:", error?.data);
2718
2783
  throw new Error(`Failed to list Supabase instances: ${error instanceof Error ? error.message : String(error)}`);
2719
2784
  }
2720
2785
  }
2721
2786
  /**
2722
2787
  * Get authentication information for a specific instance
2723
2788
  */
2724
- async getInstanceAuthInfo(instanceName) {
2789
+ async getInstanceAuthInfo(instanceName, regionId) {
2725
2790
  const request = new $RdsAi.DescribeInstanceAuthInfoRequest({
2726
2791
  instanceName
2727
2792
  });
2728
2793
  try {
2729
- const response = await this.client.describeInstanceAuthInfo(request);
2794
+ const response = await this.getClientForRegion(regionId || this.config.regionId).describeInstanceAuthInfo(request);
2730
2795
  if (!response.body) {
2731
2796
  throw new Error("Empty response from DescribeInstanceAuthInfo API");
2732
2797
  }
@@ -2760,7 +2825,7 @@ var AliyunRdsAiClient = class {
2760
2825
  if (!instance) {
2761
2826
  throw new Error(`Instance ${instanceName} not found. Available instances: ${instancesResponse.instances.map((i) => i.instanceName).join(", ")}`);
2762
2827
  }
2763
- const authInfo = await this.getInstanceAuthInfo(instanceName);
2828
+ const authInfo = await this.getInstanceAuthInfo(instanceName, instance.regionId || queryRegion);
2764
2829
  const connectionString = useVpc ? instance.vpcConnectionString : instance.publicConnectionString;
2765
2830
  const supabaseUrl = connectionString.startsWith("http") ? connectionString : `http://${connectionString}`;
2766
2831
  return {
@@ -2854,7 +2919,8 @@ import { z as z27 } from "zod";
2854
2919
  import { zodToJsonSchema as zodToJsonSchema3 } from "zod-to-json-schema";
2855
2920
  var ConnectInstanceInputZodSchema = z27.object({
2856
2921
  instance_name: z27.string().describe("The instance name (e.g., ra-supabase-8moov5lxba****)"),
2857
- use_vpc: z27.boolean().optional().default(false).describe("Whether to use VPC connection instead of public connection")
2922
+ use_vpc: z27.boolean().optional().default(false).describe("Whether to use VPC connection instead of public connection"),
2923
+ region_id: z27.string().optional().describe("Region ID of the instance (e.g., cn-hangzhou, cn-chengdu). Required when connecting to an instance in a different region than the default startup region.")
2858
2924
  });
2859
2925
  var ConnectInstanceOutputZodSchema = z27.object({
2860
2926
  success: z27.boolean(),
@@ -2869,7 +2935,7 @@ async function execute3(input, aliyunClient, createSupabaseClient, setCurrentIns
2869
2935
  }
2870
2936
  log(`Connecting to Supabase instance: ${input.instance_name}...`, "info");
2871
2937
  try {
2872
- const credentials = await aliyunClient.getSupabaseCredentials(input.instance_name, input.use_vpc);
2938
+ const credentials = await aliyunClient.getSupabaseCredentials(input.instance_name, input.use_vpc, input.region_id);
2873
2939
  log(`Retrieved credentials for instance ${input.instance_name}`, "info");
2874
2940
  log(`Supabase URL: ${credentials.supabaseUrl}`, "info");
2875
2941
  const supabaseClient = await createSupabaseClient(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliyun-rds/supabase-mcp-server",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "MCP (Model Context Protocol) server for self-hosted Supabase instances. Allows AI assistants to interact with your self-hosted Supabase database.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {