@aliyun-rds/supabase-mcp-server 1.0.3 → 1.0.5
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.
- package/README.md +303 -265
- package/dist/index.js +1060 -364
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -56,7 +56,8 @@ var SelfhostedSupabaseClient = class _SelfhostedSupabaseClient {
|
|
|
56
56
|
*/
|
|
57
57
|
constructor(options) {
|
|
58
58
|
this.options = options;
|
|
59
|
-
|
|
59
|
+
const apiKey = options.supabaseServiceRoleKey || options.supabaseAnonKey;
|
|
60
|
+
this.supabase = createClient(options.supabaseUrl, apiKey, options.supabaseClientOptions);
|
|
60
61
|
if (!options.supabaseUrl || !options.supabaseAnonKey) {
|
|
61
62
|
throw new Error("Supabase URL and Anon Key are required.");
|
|
62
63
|
}
|
|
@@ -246,61 +247,90 @@ var SelfhostedSupabaseClient = class _SelfhostedSupabaseClient {
|
|
|
246
247
|
}
|
|
247
248
|
}
|
|
248
249
|
// --- Helper/Private Methods (to be implemented) ---
|
|
250
|
+
/**
|
|
251
|
+
* Executes SQL using postgres-meta API (/pg/query endpoint)
|
|
252
|
+
* This is available in Supabase instances and doesn't require direct database connection
|
|
253
|
+
*/
|
|
254
|
+
async executeSqlViaPostgresMeta(query) {
|
|
255
|
+
const url = `${this.options.supabaseUrl}/pg/query`;
|
|
256
|
+
const apiKey = this.options.supabaseServiceRoleKey || this.options.supabaseAnonKey;
|
|
257
|
+
const response = await fetch(url, {
|
|
258
|
+
method: "POST",
|
|
259
|
+
headers: {
|
|
260
|
+
"Content-Type": "application/json",
|
|
261
|
+
"apikey": apiKey
|
|
262
|
+
},
|
|
263
|
+
body: JSON.stringify({ query })
|
|
264
|
+
});
|
|
265
|
+
if (!response.ok) {
|
|
266
|
+
const errorText = await response.text();
|
|
267
|
+
throw new Error(`postgres-meta API error: ${response.status} ${errorText}`);
|
|
268
|
+
}
|
|
269
|
+
return await response.json();
|
|
270
|
+
}
|
|
249
271
|
async checkAndCreateRpcFunction() {
|
|
250
272
|
console.error("Checking for public.execute_sql RPC function...");
|
|
273
|
+
if (!this.options.supabaseServiceRoleKey) {
|
|
274
|
+
console.error("Cannot check/create 'public.execute_sql': supabaseServiceRoleKey not provided.");
|
|
275
|
+
this.rpcFunctionExists = false;
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
251
278
|
try {
|
|
252
|
-
|
|
253
|
-
|
|
279
|
+
console.error("Checking if execute_sql function exists using postgres-meta API...");
|
|
280
|
+
const checkQuery = `
|
|
281
|
+
SELECT EXISTS (
|
|
282
|
+
SELECT 1
|
|
283
|
+
FROM pg_proc p
|
|
284
|
+
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
285
|
+
WHERE n.nspname = 'public'
|
|
286
|
+
AND p.proname = 'execute_sql'
|
|
287
|
+
) as function_exists;
|
|
288
|
+
`;
|
|
289
|
+
const checkResult = await this.executeSqlViaPostgresMeta(checkQuery);
|
|
290
|
+
if (checkResult && checkResult.length > 0 && checkResult[0].function_exists) {
|
|
254
291
|
console.error("'public.execute_sql' function found.");
|
|
255
292
|
this.rpcFunctionExists = true;
|
|
256
293
|
return;
|
|
257
294
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
295
|
+
console.error("'public.execute_sql' function not found. Creating...");
|
|
296
|
+
try {
|
|
297
|
+
console.error("Creating 'public.execute_sql' function using postgres-meta API...");
|
|
298
|
+
await this.executeSqlViaPostgresMeta(_SelfhostedSupabaseClient.CREATE_EXECUTE_SQL_FUNCTION);
|
|
299
|
+
await this.executeSqlViaPostgresMeta(_SelfhostedSupabaseClient.GRANT_EXECUTE_SQL_FUNCTION);
|
|
300
|
+
console.error("'public.execute_sql' function created and permissions granted successfully.");
|
|
301
|
+
console.error("Notifying PostgREST to reload schema cache...");
|
|
302
|
+
await this.executeSqlViaPostgresMeta("NOTIFY pgrst, 'reload schema'");
|
|
303
|
+
console.error("PostgREST schema reload notification sent.");
|
|
304
|
+
this.rpcFunctionExists = true;
|
|
305
|
+
} catch (postgresMetaError) {
|
|
306
|
+
console.error("Failed to create function via postgres-meta API:", postgresMetaError);
|
|
307
|
+
if (this.options.databaseUrl) {
|
|
308
|
+
try {
|
|
309
|
+
console.error("Falling back to direct DB connection...");
|
|
310
|
+
await this.executeSqlWithPg(_SelfhostedSupabaseClient.CREATE_EXECUTE_SQL_FUNCTION);
|
|
311
|
+
await this.executeSqlWithPg(_SelfhostedSupabaseClient.GRANT_EXECUTE_SQL_FUNCTION);
|
|
312
|
+
await this.executeSqlWithPg("NOTIFY pgrst, 'reload schema'");
|
|
313
|
+
console.error("'public.execute_sql' function created via direct DB connection.");
|
|
314
|
+
this.rpcFunctionExists = true;
|
|
315
|
+
} catch (pgError) {
|
|
316
|
+
const errorMessage = pgError instanceof Error ? pgError.message : String(pgError);
|
|
317
|
+
console.error("Failed to create function via direct DB connection:", pgError);
|
|
318
|
+
this.rpcFunctionExists = false;
|
|
319
|
+
console.error("RPC function creation failed. You can manually install using the install_execute_sql_function tool.");
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
const errorMessage = postgresMetaError instanceof Error ? postgresMetaError.message : String(postgresMetaError);
|
|
323
|
+
console.error("No fallback available (databaseUrl not provided)");
|
|
324
|
+
console.error("RPC function creation failed. You can manually install using the install_execute_sql_function tool.");
|
|
286
325
|
this.rpcFunctionExists = false;
|
|
287
|
-
throw new Error(`Failed to create execute_sql function/notify: ${errorMessage}`);
|
|
288
326
|
}
|
|
289
|
-
} else {
|
|
290
|
-
console.error(
|
|
291
|
-
"Unexpected error checking for 'public.execute_sql' function:",
|
|
292
|
-
error
|
|
293
|
-
);
|
|
294
|
-
this.rpcFunctionExists = false;
|
|
295
|
-
throw new Error(
|
|
296
|
-
`Error checking for execute_sql function: ${error.message}`
|
|
297
|
-
);
|
|
298
327
|
}
|
|
299
328
|
} catch (err) {
|
|
300
329
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
301
330
|
console.error("Exception during RPC function check/creation:", err);
|
|
331
|
+
console.error("RPC function check failed, but continuing initialization...");
|
|
332
|
+
console.error("You can manually install the execute_sql function using the install_execute_sql_function tool.");
|
|
302
333
|
this.rpcFunctionExists = false;
|
|
303
|
-
throw new Error(`Exception during RPC function check/creation: ${errorMessage}`);
|
|
304
334
|
}
|
|
305
335
|
}
|
|
306
336
|
// --- Getters ---
|
|
@@ -331,6 +361,12 @@ var SelfhostedSupabaseClient = class _SelfhostedSupabaseClient {
|
|
|
331
361
|
isPgAvailable() {
|
|
332
362
|
return !!this.options.databaseUrl;
|
|
333
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* Checks if the execute_sql RPC function is available.
|
|
366
|
+
*/
|
|
367
|
+
isRpcAvailable() {
|
|
368
|
+
return this.rpcFunctionExists;
|
|
369
|
+
}
|
|
334
370
|
};
|
|
335
371
|
|
|
336
372
|
// src/tools/list_tables.ts
|
|
@@ -405,32 +441,36 @@ var listTablesTool = {
|
|
|
405
441
|
// Use explicit types for input and context
|
|
406
442
|
execute: async (input, context) => {
|
|
407
443
|
const client = context.selfhostedClient;
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
444
|
+
console.error("Listing tables using Supabase REST API introspection...");
|
|
445
|
+
try {
|
|
446
|
+
if (client.isPgAvailable() || client.isRpcAvailable()) {
|
|
447
|
+
const listTablesSql = `
|
|
448
|
+
SELECT
|
|
449
|
+
table_schema as schema,
|
|
450
|
+
table_name as name,
|
|
451
|
+
NULL as comment
|
|
452
|
+
FROM
|
|
453
|
+
information_schema.tables
|
|
454
|
+
WHERE
|
|
455
|
+
table_type = 'BASE TABLE'
|
|
456
|
+
AND table_schema NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
|
|
457
|
+
AND table_schema NOT LIKE 'pg_temp_%'
|
|
458
|
+
AND table_schema NOT LIKE 'pg_toast_temp_%'
|
|
459
|
+
AND table_schema NOT IN ('auth', 'storage', 'extensions', 'graphql', 'graphql_public', 'pgbouncer', 'realtime', 'supabase_functions', 'supabase_migrations', '_realtime')
|
|
460
|
+
ORDER BY
|
|
461
|
+
table_schema,
|
|
462
|
+
table_name
|
|
463
|
+
`;
|
|
464
|
+
const result = await executeSqlWithFallback(client, listTablesSql, true);
|
|
465
|
+
return handleSqlResponse(result, ListTablesOutputSchema);
|
|
466
|
+
}
|
|
467
|
+
context.log("Cannot list tables: requires either direct database access (DATABASE_URL) or execute_sql RPC function", "error");
|
|
468
|
+
throw new Error("Cannot list tables: requires either direct database access (DATABASE_URL) or execute_sql RPC function. Please provide DATABASE_URL or install the execute_sql RPC function in your Supabase instance.");
|
|
469
|
+
} catch (error) {
|
|
470
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
471
|
+
context.log(`Error listing tables: ${errorMessage}`, "error");
|
|
472
|
+
throw error;
|
|
473
|
+
}
|
|
434
474
|
}
|
|
435
475
|
};
|
|
436
476
|
|
|
@@ -538,7 +578,7 @@ var mcpInputSchema4 = {
|
|
|
538
578
|
};
|
|
539
579
|
var applyMigrationTool = {
|
|
540
580
|
name: "apply_migration",
|
|
541
|
-
description: "Applies a SQL migration script and records it in the supabase_migrations.schema_migrations table within a transaction.",
|
|
581
|
+
description: "Applies a SQL migration script and records it in the supabase_migrations.schema_migrations table within a transaction. Requires direct database connection (DATABASE_URL).",
|
|
542
582
|
inputSchema: ApplyMigrationInputSchema,
|
|
543
583
|
mcpInputSchema: mcpInputSchema4,
|
|
544
584
|
outputSchema: ApplyMigrationOutputSchema,
|
|
@@ -1105,32 +1145,37 @@ var listAuthUsersTool = {
|
|
|
1105
1145
|
execute: async (input, context) => {
|
|
1106
1146
|
const client = context.selfhostedClient;
|
|
1107
1147
|
const { limit, offset } = input;
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1148
|
+
console.error("Listing auth users using Supabase Admin API...");
|
|
1149
|
+
try {
|
|
1150
|
+
const { data, error } = await client.supabase.auth.admin.listUsers({
|
|
1151
|
+
page: Math.floor(offset / limit) + 1,
|
|
1152
|
+
perPage: limit
|
|
1153
|
+
});
|
|
1154
|
+
if (error) {
|
|
1155
|
+
context.log(`Error listing users: ${error.message}`, "error");
|
|
1156
|
+
throw new Error(`Failed to list users: ${error.message}`);
|
|
1157
|
+
}
|
|
1158
|
+
if (!data || !data.users) {
|
|
1159
|
+
context.log("No users found or invalid response", "warning");
|
|
1160
|
+
return [];
|
|
1161
|
+
}
|
|
1162
|
+
const users = data.users.map((user) => ({
|
|
1163
|
+
id: user.id,
|
|
1164
|
+
email: user.email || null,
|
|
1165
|
+
role: user.role || null,
|
|
1166
|
+
created_at: user.created_at || null,
|
|
1167
|
+
last_sign_in_at: user.last_sign_in_at || null,
|
|
1168
|
+
raw_app_meta_data: user.app_metadata || null,
|
|
1169
|
+
raw_user_meta_data: user.user_metadata || null
|
|
1170
|
+
}));
|
|
1171
|
+
console.error(`Found ${users.length} users.`);
|
|
1172
|
+
context.log(`Found ${users.length} users.`);
|
|
1173
|
+
return ListAuthUsersOutputSchema.parse(users);
|
|
1174
|
+
} catch (error) {
|
|
1175
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1176
|
+
context.log(`Error listing users: ${errorMessage}`, "error");
|
|
1177
|
+
throw error;
|
|
1111
1178
|
}
|
|
1112
|
-
const listUsersSql = `
|
|
1113
|
-
SELECT
|
|
1114
|
-
id,
|
|
1115
|
-
email,
|
|
1116
|
-
role,
|
|
1117
|
-
raw_app_meta_data,
|
|
1118
|
-
raw_user_meta_data,
|
|
1119
|
-
created_at::text, -- Cast timestamp to text for JSON
|
|
1120
|
-
last_sign_in_at::text -- Cast timestamp to text for JSON
|
|
1121
|
-
FROM
|
|
1122
|
-
auth.users
|
|
1123
|
-
ORDER BY
|
|
1124
|
-
created_at DESC
|
|
1125
|
-
LIMIT ${limit}
|
|
1126
|
-
OFFSET ${offset}
|
|
1127
|
-
`;
|
|
1128
|
-
console.error("Attempting to list auth users using direct DB connection...");
|
|
1129
|
-
const result = await client.executeSqlWithPg(listUsersSql);
|
|
1130
|
-
const validatedUsers = handleSqlResponse(result, ListAuthUsersOutputSchema);
|
|
1131
|
-
console.error(`Found ${validatedUsers.length} users.`);
|
|
1132
|
-
context.log(`Found ${validatedUsers.length} users.`);
|
|
1133
|
-
return validatedUsers;
|
|
1134
1179
|
}
|
|
1135
1180
|
};
|
|
1136
1181
|
|
|
@@ -1171,43 +1216,33 @@ var getAuthUserTool = {
|
|
|
1171
1216
|
execute: async (input, context) => {
|
|
1172
1217
|
const client = context.selfhostedClient;
|
|
1173
1218
|
const { user_id } = input;
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
id,
|
|
1181
|
-
email,
|
|
1182
|
-
role,
|
|
1183
|
-
raw_app_meta_data,
|
|
1184
|
-
raw_user_meta_data,
|
|
1185
|
-
created_at::text,
|
|
1186
|
-
last_sign_in_at::text
|
|
1187
|
-
FROM auth.users
|
|
1188
|
-
WHERE id = $1
|
|
1189
|
-
`;
|
|
1190
|
-
const params = [user_id];
|
|
1191
|
-
console.error(`Attempting to get auth user ${user_id} using direct DB connection...`);
|
|
1192
|
-
const user = await client.executeTransactionWithPg(async (pgClient) => {
|
|
1193
|
-
const result = await pgClient.query(sql, params);
|
|
1194
|
-
if (result.rows.length === 0) {
|
|
1195
|
-
throw new Error(`User with ID ${user_id} not found.`);
|
|
1219
|
+
console.error(`Getting auth user ${user_id} using Supabase Admin API...`);
|
|
1220
|
+
try {
|
|
1221
|
+
const { data, error } = await client.supabase.auth.admin.getUserById(user_id);
|
|
1222
|
+
if (error) {
|
|
1223
|
+
context.log(`Error getting user: ${error.message}`, "error");
|
|
1224
|
+
throw new Error(`Failed to get user: ${error.message}`);
|
|
1196
1225
|
}
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
return singleUser;
|
|
1200
|
-
} catch (validationError) {
|
|
1201
|
-
if (validationError instanceof z16.ZodError) {
|
|
1202
|
-
console.error("Zod validation failed:", validationError.errors);
|
|
1203
|
-
throw new Error(`Output validation failed: ${validationError.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ")}`);
|
|
1204
|
-
}
|
|
1205
|
-
throw validationError;
|
|
1226
|
+
if (!data || !data.user) {
|
|
1227
|
+
throw new Error(`User with ID ${user_id} not found.`);
|
|
1206
1228
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1229
|
+
const user = {
|
|
1230
|
+
id: data.user.id,
|
|
1231
|
+
email: data.user.email || null,
|
|
1232
|
+
role: data.user.role || null,
|
|
1233
|
+
created_at: data.user.created_at || null,
|
|
1234
|
+
last_sign_in_at: data.user.last_sign_in_at || null,
|
|
1235
|
+
raw_app_meta_data: data.user.app_metadata || null,
|
|
1236
|
+
raw_user_meta_data: data.user.user_metadata || null
|
|
1237
|
+
};
|
|
1238
|
+
console.error(`Found user ${user_id}.`);
|
|
1239
|
+
context.log(`Found user ${user_id}.`);
|
|
1240
|
+
return AuthUserZodSchema2.parse(user);
|
|
1241
|
+
} catch (error) {
|
|
1242
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1243
|
+
context.log(`Error getting user: ${errorMessage}`, "error");
|
|
1244
|
+
throw error;
|
|
1245
|
+
}
|
|
1211
1246
|
}
|
|
1212
1247
|
};
|
|
1213
1248
|
|
|
@@ -1233,37 +1268,34 @@ var mcpInputSchema16 = {
|
|
|
1233
1268
|
};
|
|
1234
1269
|
var deleteAuthUserTool = {
|
|
1235
1270
|
name: "delete_auth_user",
|
|
1236
|
-
description: "Deletes a user
|
|
1271
|
+
description: "Deletes a user using Supabase Admin API. Requires service role key.",
|
|
1237
1272
|
inputSchema: DeleteAuthUserInputSchema,
|
|
1238
1273
|
mcpInputSchema: mcpInputSchema16,
|
|
1239
1274
|
outputSchema: DeleteAuthUserOutputSchema,
|
|
1240
1275
|
execute: async (input, context) => {
|
|
1241
1276
|
const client = context.selfhostedClient;
|
|
1242
1277
|
const { user_id } = input;
|
|
1243
|
-
if (!client
|
|
1244
|
-
throw new Error("
|
|
1278
|
+
if (!client) {
|
|
1279
|
+
throw new Error("Supabase client is not initialized.");
|
|
1245
1280
|
}
|
|
1281
|
+
context.log(`Deleting user ${user_id} using Supabase Admin API...`, "info");
|
|
1246
1282
|
try {
|
|
1247
|
-
const
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
[user_id]
|
|
1251
|
-
);
|
|
1252
|
-
return deleteResult;
|
|
1253
|
-
});
|
|
1254
|
-
if (result.rowCount === 1) {
|
|
1283
|
+
const { error } = await client.supabase.auth.admin.deleteUser(user_id);
|
|
1284
|
+
if (error) {
|
|
1285
|
+
context.log(`Error deleting user: ${error.message}`, "error");
|
|
1255
1286
|
return {
|
|
1256
|
-
success:
|
|
1257
|
-
message: `
|
|
1287
|
+
success: false,
|
|
1288
|
+
message: `Failed to delete user: ${error.message}`
|
|
1258
1289
|
};
|
|
1259
1290
|
}
|
|
1291
|
+
context.log(`User ${user_id} deleted successfully`, "info");
|
|
1260
1292
|
return {
|
|
1261
|
-
success:
|
|
1262
|
-
message: `
|
|
1293
|
+
success: true,
|
|
1294
|
+
message: `Successfully deleted user with ID: ${user_id}`
|
|
1263
1295
|
};
|
|
1264
1296
|
} catch (error) {
|
|
1265
1297
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1266
|
-
|
|
1298
|
+
context.log(`Error deleting user ${user_id}: ${errorMessage}`, "error");
|
|
1267
1299
|
throw new Error(`Failed to delete user ${user_id}: ${errorMessage}`);
|
|
1268
1300
|
}
|
|
1269
1301
|
}
|
|
@@ -1302,80 +1334,48 @@ var mcpInputSchema17 = {
|
|
|
1302
1334
|
};
|
|
1303
1335
|
var createAuthUserTool = {
|
|
1304
1336
|
name: "create_auth_user",
|
|
1305
|
-
description: "Creates a new user
|
|
1337
|
+
description: "Creates a new user using Supabase Admin API. Requires service role key.",
|
|
1306
1338
|
inputSchema: CreateAuthUserInputSchema,
|
|
1307
1339
|
mcpInputSchema: mcpInputSchema17,
|
|
1308
|
-
// Ensure defined above
|
|
1309
1340
|
outputSchema: CreatedAuthUserZodSchema,
|
|
1310
1341
|
execute: async (input, context) => {
|
|
1311
1342
|
const client = context.selfhostedClient;
|
|
1312
1343
|
const { email, password, role, app_metadata, user_metadata } = input;
|
|
1313
|
-
if (!client
|
|
1314
|
-
|
|
1315
|
-
throw new Error("Direct database connection (DATABASE_URL) is required to create an auth user directly.");
|
|
1344
|
+
if (!client) {
|
|
1345
|
+
throw new Error("Supabase client is not initialized.");
|
|
1316
1346
|
}
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
try {
|
|
1321
|
-
await pgClient.query("SELECT crypt('test', gen_salt('bf'))");
|
|
1322
|
-
} catch (err) {
|
|
1323
|
-
throw new Error("Failed to execute crypt function. Ensure pgcrypto extension is enabled in the database.");
|
|
1324
|
-
}
|
|
1325
|
-
const sql = `
|
|
1326
|
-
INSERT INTO auth.users (
|
|
1327
|
-
instance_id, email, encrypted_password, role,
|
|
1328
|
-
raw_app_meta_data, raw_user_meta_data,
|
|
1329
|
-
aud, email_confirmed_at, confirmation_sent_at -- Set required defaults
|
|
1330
|
-
)
|
|
1331
|
-
VALUES (
|
|
1332
|
-
COALESCE(current_setting('app.instance_id', TRUE), '00000000-0000-0000-0000-000000000000')::uuid,
|
|
1333
|
-
$1, crypt($2, gen_salt('bf')),
|
|
1334
|
-
$3,
|
|
1335
|
-
$4::jsonb,
|
|
1336
|
-
$5::jsonb,
|
|
1337
|
-
'authenticated', now(), now()
|
|
1338
|
-
)
|
|
1339
|
-
RETURNING id, email, role, raw_app_meta_data, raw_user_meta_data, created_at::text, last_sign_in_at::text;
|
|
1340
|
-
`;
|
|
1341
|
-
const params = [
|
|
1347
|
+
context.log(`Creating user ${email} using Supabase Admin API...`, "info");
|
|
1348
|
+
try {
|
|
1349
|
+
const { data, error } = await client.supabase.auth.admin.createUser({
|
|
1342
1350
|
email,
|
|
1343
1351
|
password,
|
|
1344
|
-
|
|
1345
|
-
//
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
throw new Error("User creation failed, no user returned after insert.");
|
|
1353
|
-
}
|
|
1354
|
-
return CreatedAuthUserZodSchema.parse(result.rows[0]);
|
|
1355
|
-
} catch (dbError) {
|
|
1356
|
-
let errorMessage = "Unknown database error during user creation";
|
|
1357
|
-
let isUniqueViolation = false;
|
|
1358
|
-
if (typeof dbError === "object" && dbError !== null && "code" in dbError) {
|
|
1359
|
-
if (dbError.code === "23505") {
|
|
1360
|
-
isUniqueViolation = true;
|
|
1361
|
-
errorMessage = `User creation failed: Email '${email}' likely already exists.`;
|
|
1362
|
-
} else if ("message" in dbError && typeof dbError.message === "string") {
|
|
1363
|
-
errorMessage = `Database error (${dbError.code}): ${dbError.message}`;
|
|
1364
|
-
} else {
|
|
1365
|
-
errorMessage = `Database error code: ${dbError.code}`;
|
|
1366
|
-
}
|
|
1367
|
-
} else if (dbError instanceof Error) {
|
|
1368
|
-
errorMessage = `Database error during user creation: ${dbError.message}`;
|
|
1369
|
-
} else {
|
|
1370
|
-
errorMessage = `Database error during user creation: ${String(dbError)}`;
|
|
1371
|
-
}
|
|
1372
|
-
console.error("Error creating user in DB:", dbError);
|
|
1373
|
-
throw new Error(errorMessage);
|
|
1352
|
+
email_confirm: true,
|
|
1353
|
+
// Auto-confirm email
|
|
1354
|
+
user_metadata: user_metadata || {},
|
|
1355
|
+
app_metadata: app_metadata || {}
|
|
1356
|
+
});
|
|
1357
|
+
if (error) {
|
|
1358
|
+
context.log(`Error creating user: ${error.message}`, "error");
|
|
1359
|
+
throw new Error(`Failed to create user: ${error.message}`);
|
|
1374
1360
|
}
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1361
|
+
if (!data.user) {
|
|
1362
|
+
throw new Error("User creation succeeded but no user data returned");
|
|
1363
|
+
}
|
|
1364
|
+
context.log(`User ${email} created successfully with ID: ${data.user.id}`, "info");
|
|
1365
|
+
return {
|
|
1366
|
+
id: data.user.id,
|
|
1367
|
+
email: data.user.email || null,
|
|
1368
|
+
role: data.user.role || role || "authenticated",
|
|
1369
|
+
created_at: data.user.created_at || null,
|
|
1370
|
+
last_sign_in_at: data.user.last_sign_in_at || null,
|
|
1371
|
+
raw_app_meta_data: data.user.app_metadata || null,
|
|
1372
|
+
raw_user_meta_data: data.user.user_metadata || null
|
|
1373
|
+
};
|
|
1374
|
+
} catch (error) {
|
|
1375
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1376
|
+
context.log(`Error creating user: ${errorMessage}`, "error");
|
|
1377
|
+
throw error;
|
|
1378
|
+
}
|
|
1379
1379
|
}
|
|
1380
1380
|
};
|
|
1381
1381
|
|
|
@@ -1417,95 +1417,63 @@ var mcpInputSchema18 = {
|
|
|
1417
1417
|
};
|
|
1418
1418
|
var updateAuthUserTool = {
|
|
1419
1419
|
name: "update_auth_user",
|
|
1420
|
-
description: "Updates fields for a user
|
|
1420
|
+
description: "Updates fields for a user using Supabase Admin API. Requires service role key.",
|
|
1421
1421
|
inputSchema: UpdateAuthUserInputSchema,
|
|
1422
1422
|
mcpInputSchema: mcpInputSchema18,
|
|
1423
|
-
// Ensure defined
|
|
1424
1423
|
outputSchema: UpdatedAuthUserZodSchema,
|
|
1425
1424
|
execute: async (input, context) => {
|
|
1426
1425
|
const client = context.selfhostedClient;
|
|
1427
1426
|
const { user_id, email, password, role, app_metadata, user_metadata } = input;
|
|
1428
|
-
if (!client
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
params.push(email);
|
|
1438
|
-
}
|
|
1439
|
-
if (password !== void 0) {
|
|
1440
|
-
updates.push(`encrypted_password = crypt($${paramIndex++}, gen_salt('bf'))`);
|
|
1441
|
-
params.push(password);
|
|
1442
|
-
console.warn(`SECURITY WARNING: Updating password for user ${user_id} with plain text password via direct DB update.`);
|
|
1443
|
-
}
|
|
1444
|
-
if (role !== void 0) {
|
|
1445
|
-
updates.push(`role = $${paramIndex++}`);
|
|
1446
|
-
params.push(role);
|
|
1447
|
-
}
|
|
1448
|
-
if (app_metadata !== void 0) {
|
|
1449
|
-
updates.push(`raw_app_meta_data = $${paramIndex++}::jsonb`);
|
|
1450
|
-
params.push(JSON.stringify(app_metadata));
|
|
1451
|
-
}
|
|
1452
|
-
if (user_metadata !== void 0) {
|
|
1453
|
-
updates.push(`raw_user_meta_data = $${paramIndex++}::jsonb`);
|
|
1454
|
-
params.push(JSON.stringify(user_metadata));
|
|
1455
|
-
}
|
|
1456
|
-
params.push(user_id);
|
|
1457
|
-
const userIdParamIndex = paramIndex;
|
|
1458
|
-
const sql = `
|
|
1459
|
-
UPDATE auth.users
|
|
1460
|
-
SET ${updates.join(", ")}, updated_at = NOW()
|
|
1461
|
-
WHERE id = $${userIdParamIndex}
|
|
1462
|
-
RETURNING id, email, role, raw_app_meta_data, raw_user_meta_data, created_at::text, updated_at::text, last_sign_in_at::text;
|
|
1463
|
-
`;
|
|
1464
|
-
console.error(`Attempting to update auth user ${user_id}...`);
|
|
1465
|
-
context.log(`Attempting to update auth user ${user_id}...`);
|
|
1466
|
-
const updatedUser = await client.executeTransactionWithPg(async (pgClient) => {
|
|
1427
|
+
if (!client) {
|
|
1428
|
+
throw new Error("Supabase client is not initialized.");
|
|
1429
|
+
}
|
|
1430
|
+
context.log(`Updating user ${user_id} using Supabase Admin API...`, "info");
|
|
1431
|
+
try {
|
|
1432
|
+
const updateAttributes = {};
|
|
1433
|
+
if (email !== void 0) {
|
|
1434
|
+
updateAttributes.email = email;
|
|
1435
|
+
}
|
|
1467
1436
|
if (password !== void 0) {
|
|
1468
|
-
|
|
1469
|
-
await pgClient.query("SELECT crypt('test', gen_salt('bf'))");
|
|
1470
|
-
} catch (err) {
|
|
1471
|
-
throw new Error("Failed to execute crypt function for password update. Ensure pgcrypto extension is enabled.");
|
|
1472
|
-
}
|
|
1437
|
+
updateAttributes.password = password;
|
|
1473
1438
|
}
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
if (result.rows.length === 0) {
|
|
1477
|
-
throw new Error(`User update failed: User with ID ${user_id} not found or no rows affected.`);
|
|
1478
|
-
}
|
|
1479
|
-
return UpdatedAuthUserZodSchema.parse(result.rows[0]);
|
|
1480
|
-
} catch (dbError) {
|
|
1481
|
-
let errorMessage = "Unknown database error during user update";
|
|
1482
|
-
let isUniqueViolation = false;
|
|
1483
|
-
if (typeof dbError === "object" && dbError !== null && "code" in dbError) {
|
|
1484
|
-
if (email !== void 0 && dbError.code === "23505") {
|
|
1485
|
-
isUniqueViolation = true;
|
|
1486
|
-
errorMessage = `User update failed: Email '${email}' likely already exists for another user.`;
|
|
1487
|
-
} else if ("message" in dbError && typeof dbError.message === "string") {
|
|
1488
|
-
errorMessage = `Database error (${dbError.code}): ${dbError.message}`;
|
|
1489
|
-
} else {
|
|
1490
|
-
errorMessage = `Database error code: ${dbError.code}`;
|
|
1491
|
-
}
|
|
1492
|
-
} else if (dbError instanceof Error) {
|
|
1493
|
-
errorMessage = `Database error during user update: ${dbError.message}`;
|
|
1494
|
-
} else {
|
|
1495
|
-
errorMessage = `Database error during user update: ${String(dbError)}`;
|
|
1496
|
-
}
|
|
1497
|
-
console.error("Error updating user in DB:", dbError);
|
|
1498
|
-
throw new Error(errorMessage);
|
|
1439
|
+
if (user_metadata !== void 0) {
|
|
1440
|
+
updateAttributes.user_metadata = user_metadata;
|
|
1499
1441
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1442
|
+
if (app_metadata !== void 0) {
|
|
1443
|
+
updateAttributes.app_metadata = app_metadata;
|
|
1444
|
+
}
|
|
1445
|
+
const { data, error } = await client.supabase.auth.admin.updateUserById(
|
|
1446
|
+
user_id,
|
|
1447
|
+
updateAttributes
|
|
1448
|
+
);
|
|
1449
|
+
if (error) {
|
|
1450
|
+
context.log(`Error updating user: ${error.message}`, "error");
|
|
1451
|
+
throw new Error(`Failed to update user: ${error.message}`);
|
|
1452
|
+
}
|
|
1453
|
+
if (!data.user) {
|
|
1454
|
+
throw new Error("User update succeeded but no user data returned");
|
|
1455
|
+
}
|
|
1456
|
+
context.log(`User ${user_id} updated successfully`, "info");
|
|
1457
|
+
return {
|
|
1458
|
+
id: data.user.id,
|
|
1459
|
+
email: data.user.email || null,
|
|
1460
|
+
role: data.user.role || role || null,
|
|
1461
|
+
created_at: data.user.created_at || null,
|
|
1462
|
+
updated_at: data.user.updated_at || null,
|
|
1463
|
+
last_sign_in_at: data.user.last_sign_in_at || null,
|
|
1464
|
+
raw_app_meta_data: data.user.app_metadata || null,
|
|
1465
|
+
raw_user_meta_data: data.user.user_metadata || null
|
|
1466
|
+
};
|
|
1467
|
+
} catch (error) {
|
|
1468
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1469
|
+
context.log(`Error updating user ${user_id}: ${errorMessage}`, "error");
|
|
1470
|
+
throw error;
|
|
1471
|
+
}
|
|
1504
1472
|
}
|
|
1505
1473
|
};
|
|
1506
1474
|
|
|
1507
1475
|
// src/index.ts
|
|
1508
|
-
import { z as
|
|
1476
|
+
import { z as z30 } from "zod";
|
|
1509
1477
|
|
|
1510
1478
|
// src/tools/list_storage_buckets.ts
|
|
1511
1479
|
import { z as z20 } from "zod";
|
|
@@ -1537,10 +1505,6 @@ var listStorageBucketsTool = {
|
|
|
1537
1505
|
execute: async (input, context) => {
|
|
1538
1506
|
const client = context.selfhostedClient;
|
|
1539
1507
|
console.error("Listing storage buckets...");
|
|
1540
|
-
if (!client.isPgAvailable()) {
|
|
1541
|
-
context.log("Direct database connection (DATABASE_URL) is required to list storage buckets.", "error");
|
|
1542
|
-
throw new Error("Direct database connection (DATABASE_URL) is required to list storage buckets.");
|
|
1543
|
-
}
|
|
1544
1508
|
const sql = `
|
|
1545
1509
|
SELECT
|
|
1546
1510
|
id,
|
|
@@ -1554,8 +1518,17 @@ var listStorageBucketsTool = {
|
|
|
1554
1518
|
updated_at::text -- Cast to text
|
|
1555
1519
|
FROM storage.buckets;
|
|
1556
1520
|
`;
|
|
1557
|
-
|
|
1558
|
-
|
|
1521
|
+
let result;
|
|
1522
|
+
if (client.isRpcAvailable()) {
|
|
1523
|
+
console.error("Listing storage buckets using execute_sql RPC function...");
|
|
1524
|
+
result = await client.executeSqlViaRpc(sql);
|
|
1525
|
+
} else if (client.isPgAvailable()) {
|
|
1526
|
+
console.error("Listing storage buckets using direct DB connection...");
|
|
1527
|
+
result = await client.executeSqlWithPg(sql);
|
|
1528
|
+
} else {
|
|
1529
|
+
context.log("Cannot list storage buckets: requires either direct database access (DATABASE_URL) or execute_sql RPC function.", "error");
|
|
1530
|
+
throw new Error("Cannot list storage buckets: requires either direct database access (DATABASE_URL) or execute_sql RPC function.");
|
|
1531
|
+
}
|
|
1559
1532
|
const validatedBuckets = handleSqlResponse(result, ListStorageBucketsOutputSchema);
|
|
1560
1533
|
console.error(`Found ${validatedBuckets.length} buckets.`);
|
|
1561
1534
|
context.log(`Found ${validatedBuckets.length} buckets.`);
|
|
@@ -1610,43 +1583,43 @@ var listStorageObjectsTool = {
|
|
|
1610
1583
|
const client = context.selfhostedClient;
|
|
1611
1584
|
const { bucket_id, limit, offset, prefix } = input;
|
|
1612
1585
|
console.error(`Listing objects for bucket ${bucket_id} (Prefix: ${prefix || "N/A"})...`);
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1586
|
+
const escapeSqlString = (str) => {
|
|
1587
|
+
return str.replace(/'/g, "''");
|
|
1588
|
+
};
|
|
1589
|
+
let sql = `
|
|
1590
|
+
SELECT
|
|
1591
|
+
id,
|
|
1592
|
+
name,
|
|
1593
|
+
bucket_id,
|
|
1594
|
+
owner,
|
|
1595
|
+
version,
|
|
1596
|
+
metadata ->> 'mimetype' AS mimetype,
|
|
1597
|
+
metadata ->> 'size' AS size,
|
|
1598
|
+
metadata,
|
|
1599
|
+
created_at::text,
|
|
1600
|
+
updated_at::text,
|
|
1601
|
+
last_accessed_at::text
|
|
1602
|
+
FROM storage.objects
|
|
1603
|
+
WHERE bucket_id = '${escapeSqlString(bucket_id)}'
|
|
1604
|
+
`;
|
|
1605
|
+
if (prefix) {
|
|
1606
|
+
sql += ` AND name LIKE '${escapeSqlString(prefix)}%'`;
|
|
1607
|
+
}
|
|
1608
|
+
sql += " ORDER BY name ASC NULLS FIRST";
|
|
1609
|
+
sql += ` LIMIT ${limit}`;
|
|
1610
|
+
sql += ` OFFSET ${offset};`;
|
|
1611
|
+
let result;
|
|
1612
|
+
if (client.isRpcAvailable()) {
|
|
1613
|
+
console.error("Listing storage objects using execute_sql RPC function...");
|
|
1614
|
+
result = await client.executeSqlViaRpc(sql);
|
|
1615
|
+
} else if (client.isPgAvailable()) {
|
|
1616
|
+
console.error("Listing storage objects using direct DB connection...");
|
|
1617
|
+
result = await client.executeSqlWithPg(sql);
|
|
1618
|
+
} else {
|
|
1619
|
+
context.log("Cannot list storage objects: requires either direct database access (DATABASE_URL) or execute_sql RPC function.", "error");
|
|
1620
|
+
throw new Error("Cannot list storage objects: requires either direct database access (DATABASE_URL) or execute_sql RPC function.");
|
|
1621
|
+
}
|
|
1622
|
+
const objects = handleSqlResponse(result, ListStorageObjectsOutputSchema);
|
|
1650
1623
|
console.error(`Found ${objects.length} objects.`);
|
|
1651
1624
|
context.log(`Found ${objects.length} objects.`);
|
|
1652
1625
|
return objects;
|
|
@@ -1685,10 +1658,6 @@ var listRealtimePublicationsTool = {
|
|
|
1685
1658
|
execute: async (input, context) => {
|
|
1686
1659
|
const client = context.selfhostedClient;
|
|
1687
1660
|
console.error("Listing Realtime publications...");
|
|
1688
|
-
if (!client.isPgAvailable()) {
|
|
1689
|
-
context.log("Direct database connection (DATABASE_URL) is required to list publications.", "error");
|
|
1690
|
-
throw new Error("Direct database connection (DATABASE_URL) is required to list publications.");
|
|
1691
|
-
}
|
|
1692
1661
|
const sql = `
|
|
1693
1662
|
SELECT
|
|
1694
1663
|
oid,
|
|
@@ -1702,8 +1671,17 @@ var listRealtimePublicationsTool = {
|
|
|
1702
1671
|
pubviaroot
|
|
1703
1672
|
FROM pg_catalog.pg_publication;
|
|
1704
1673
|
`;
|
|
1705
|
-
|
|
1706
|
-
|
|
1674
|
+
let result;
|
|
1675
|
+
if (client.isRpcAvailable()) {
|
|
1676
|
+
console.error("Listing publications using execute_sql RPC function...");
|
|
1677
|
+
result = await client.executeSqlViaRpc(sql);
|
|
1678
|
+
} else if (client.isPgAvailable()) {
|
|
1679
|
+
console.error("Listing publications using direct DB connection...");
|
|
1680
|
+
result = await client.executeSqlWithPg(sql);
|
|
1681
|
+
} else {
|
|
1682
|
+
context.log("Cannot list publications: requires either direct database access (DATABASE_URL) or execute_sql RPC function.", "error");
|
|
1683
|
+
throw new Error("Cannot list publications: requires either direct database access (DATABASE_URL) or execute_sql RPC function.");
|
|
1684
|
+
}
|
|
1707
1685
|
const validatedPublications = handleSqlResponse(result, ListRealtimePublicationsOutputSchema);
|
|
1708
1686
|
console.error(`Found ${validatedPublications.length} publications.`);
|
|
1709
1687
|
context.log(`Found ${validatedPublications.length} publications.`);
|
|
@@ -1712,6 +1690,256 @@ var listRealtimePublicationsTool = {
|
|
|
1712
1690
|
};
|
|
1713
1691
|
var list_realtime_publications_default = listRealtimePublicationsTool;
|
|
1714
1692
|
|
|
1693
|
+
// src/tools/install_execute_sql_function.ts
|
|
1694
|
+
import { z as z23 } from "zod";
|
|
1695
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
1696
|
+
var InstallExecuteSqlFunctionInputZodSchema = z23.object({});
|
|
1697
|
+
var InstallExecuteSqlFunctionOutputZodSchema = z23.object({
|
|
1698
|
+
success: z23.boolean(),
|
|
1699
|
+
message: z23.string()
|
|
1700
|
+
});
|
|
1701
|
+
var CREATE_EXECUTE_SQL_FUNCTION = `
|
|
1702
|
+
CREATE OR REPLACE FUNCTION public.execute_sql(query text, read_only boolean DEFAULT true)
|
|
1703
|
+
RETURNS jsonb
|
|
1704
|
+
LANGUAGE plpgsql
|
|
1705
|
+
SECURITY DEFINER
|
|
1706
|
+
SET search_path = public
|
|
1707
|
+
AS $$
|
|
1708
|
+
DECLARE
|
|
1709
|
+
result jsonb;
|
|
1710
|
+
BEGIN
|
|
1711
|
+
-- Execute query and convert result to JSON
|
|
1712
|
+
IF read_only THEN
|
|
1713
|
+
-- Read-only query
|
|
1714
|
+
EXECUTE 'SELECT COALESCE(jsonb_agg(row_to_json(t)), ''[]''::jsonb) FROM (' || query || ') t' INTO result;
|
|
1715
|
+
ELSE
|
|
1716
|
+
-- Write query (INSERT/UPDATE/DELETE)
|
|
1717
|
+
EXECUTE query;
|
|
1718
|
+
result := '[]'::jsonb;
|
|
1719
|
+
END IF;
|
|
1720
|
+
|
|
1721
|
+
RETURN COALESCE(result, '[]'::jsonb);
|
|
1722
|
+
EXCEPTION
|
|
1723
|
+
WHEN OTHERS THEN
|
|
1724
|
+
-- Return error information
|
|
1725
|
+
RETURN jsonb_build_object(
|
|
1726
|
+
'error', SQLERRM,
|
|
1727
|
+
'code', SQLSTATE
|
|
1728
|
+
);
|
|
1729
|
+
END;
|
|
1730
|
+
$$;
|
|
1731
|
+
`;
|
|
1732
|
+
var GRANT_EXECUTE_SQL_FUNCTION = `
|
|
1733
|
+
GRANT EXECUTE ON FUNCTION public.execute_sql(text, boolean) TO anon, authenticated, service_role;
|
|
1734
|
+
`;
|
|
1735
|
+
var NOTIFY_POSTGREST = `NOTIFY pgrst, 'reload schema';`;
|
|
1736
|
+
async function execute(input, context) {
|
|
1737
|
+
const client = context.selfhostedClient;
|
|
1738
|
+
context.log("Installing execute_sql RPC function...", "info");
|
|
1739
|
+
try {
|
|
1740
|
+
const supabaseUrl = client.getSupabaseUrl();
|
|
1741
|
+
const serviceKey = client.getServiceRoleKey();
|
|
1742
|
+
if (!serviceKey) {
|
|
1743
|
+
throw new Error("Service role key is required to install the execute_sql function");
|
|
1744
|
+
}
|
|
1745
|
+
const postgresMetaUrl = `${supabaseUrl}/pg/query`;
|
|
1746
|
+
context.log("Creating execute_sql function...", "info");
|
|
1747
|
+
const createResponse = await fetch(postgresMetaUrl, {
|
|
1748
|
+
method: "POST",
|
|
1749
|
+
headers: {
|
|
1750
|
+
"Content-Type": "application/json",
|
|
1751
|
+
"apikey": serviceKey
|
|
1752
|
+
},
|
|
1753
|
+
body: JSON.stringify({ query: CREATE_EXECUTE_SQL_FUNCTION })
|
|
1754
|
+
});
|
|
1755
|
+
if (!createResponse.ok) {
|
|
1756
|
+
const errorText = await createResponse.text();
|
|
1757
|
+
throw new Error(`Failed to create function: ${createResponse.status} ${errorText}`);
|
|
1758
|
+
}
|
|
1759
|
+
context.log("Granting permissions...", "info");
|
|
1760
|
+
const grantResponse = await fetch(postgresMetaUrl, {
|
|
1761
|
+
method: "POST",
|
|
1762
|
+
headers: {
|
|
1763
|
+
"Content-Type": "application/json",
|
|
1764
|
+
"apikey": serviceKey
|
|
1765
|
+
},
|
|
1766
|
+
body: JSON.stringify({ query: GRANT_EXECUTE_SQL_FUNCTION })
|
|
1767
|
+
});
|
|
1768
|
+
if (!grantResponse.ok) {
|
|
1769
|
+
const errorText = await grantResponse.text();
|
|
1770
|
+
throw new Error(`Failed to grant permissions: ${grantResponse.status} ${errorText}`);
|
|
1771
|
+
}
|
|
1772
|
+
context.log("Notifying PostgREST to reload schema...", "info");
|
|
1773
|
+
const notifyResponse = await fetch(postgresMetaUrl, {
|
|
1774
|
+
method: "POST",
|
|
1775
|
+
headers: {
|
|
1776
|
+
"Content-Type": "application/json",
|
|
1777
|
+
"apikey": serviceKey
|
|
1778
|
+
},
|
|
1779
|
+
body: JSON.stringify({ query: NOTIFY_POSTGREST })
|
|
1780
|
+
});
|
|
1781
|
+
if (!notifyResponse.ok) {
|
|
1782
|
+
const errorText = await notifyResponse.text();
|
|
1783
|
+
console.error(`Warning: Failed to notify PostgREST: ${notifyResponse.status} ${errorText}`);
|
|
1784
|
+
}
|
|
1785
|
+
context.log("execute_sql function installed successfully!", "info");
|
|
1786
|
+
context.log("You may need to wait a few seconds for PostgREST to reload the schema.", "info");
|
|
1787
|
+
return {
|
|
1788
|
+
success: true,
|
|
1789
|
+
message: "execute_sql function installed successfully. You can now use tools that require SQL execution (list_tables, execute_sql, etc.)."
|
|
1790
|
+
};
|
|
1791
|
+
} catch (error) {
|
|
1792
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1793
|
+
context.log(`Error installing execute_sql function: ${errorMessage}`, "error");
|
|
1794
|
+
throw new Error(`Failed to install execute_sql function: ${errorMessage}`);
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
var installExecuteSqlFunctionTool = {
|
|
1798
|
+
name: "install_execute_sql_function",
|
|
1799
|
+
description: "Install the execute_sql RPC function in the connected Supabase instance. This function is required for tools that need to execute SQL queries (list_tables, list_extensions, execute_sql, etc.). This tool uses the postgres-meta API and does not require direct database access.",
|
|
1800
|
+
inputSchema: InstallExecuteSqlFunctionInputZodSchema,
|
|
1801
|
+
mcpInputSchema: zodToJsonSchema(InstallExecuteSqlFunctionInputZodSchema),
|
|
1802
|
+
outputSchema: InstallExecuteSqlFunctionOutputZodSchema,
|
|
1803
|
+
execute
|
|
1804
|
+
};
|
|
1805
|
+
var install_execute_sql_function_default = installExecuteSqlFunctionTool;
|
|
1806
|
+
|
|
1807
|
+
// src/tools/search_docs.ts
|
|
1808
|
+
import { z as z24 } from "zod";
|
|
1809
|
+
|
|
1810
|
+
// src/integrations/content-api-client.ts
|
|
1811
|
+
var SupabaseDocsApiClient = class {
|
|
1812
|
+
schemaCache = null;
|
|
1813
|
+
graphqlEndpoint;
|
|
1814
|
+
constructor(baseUrl = "https://supabase.com/docs/api/graphql") {
|
|
1815
|
+
this.graphqlEndpoint = baseUrl;
|
|
1816
|
+
}
|
|
1817
|
+
/**
|
|
1818
|
+
* Load the GraphQL schema for Supabase documentation
|
|
1819
|
+
* Results are cached to avoid repeated requests
|
|
1820
|
+
*/
|
|
1821
|
+
async loadSchema() {
|
|
1822
|
+
if (this.schemaCache) {
|
|
1823
|
+
console.error("Using cached GraphQL schema");
|
|
1824
|
+
return this.schemaCache;
|
|
1825
|
+
}
|
|
1826
|
+
console.error(`Fetching GraphQL schema from ${this.graphqlEndpoint}...`);
|
|
1827
|
+
try {
|
|
1828
|
+
const response = await fetch(this.graphqlEndpoint, {
|
|
1829
|
+
method: "POST",
|
|
1830
|
+
headers: {
|
|
1831
|
+
"Content-Type": "application/json",
|
|
1832
|
+
"Accept": "application/json"
|
|
1833
|
+
},
|
|
1834
|
+
body: JSON.stringify({
|
|
1835
|
+
query: "{ schema }"
|
|
1836
|
+
})
|
|
1837
|
+
});
|
|
1838
|
+
if (!response.ok) {
|
|
1839
|
+
throw new Error(`Failed to fetch schema: ${response.status} ${response.statusText}`);
|
|
1840
|
+
}
|
|
1841
|
+
const result = await response.json();
|
|
1842
|
+
if (result.errors) {
|
|
1843
|
+
throw new Error(
|
|
1844
|
+
`GraphQL errors: ${result.errors.map((e) => e.message).join(", ")}`
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1847
|
+
if (!result.data || !result.data.schema) {
|
|
1848
|
+
throw new Error("Schema not found in GraphQL response");
|
|
1849
|
+
}
|
|
1850
|
+
this.schemaCache = result.data.schema;
|
|
1851
|
+
console.error("GraphQL schema loaded and cached successfully");
|
|
1852
|
+
return this.schemaCache;
|
|
1853
|
+
} catch (error) {
|
|
1854
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1855
|
+
console.error(`Error loading GraphQL schema: ${errorMessage}`);
|
|
1856
|
+
throw new Error(`Failed to load Supabase documentation schema: ${errorMessage}`);
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* Execute a GraphQL query against Supabase documentation
|
|
1861
|
+
*/
|
|
1862
|
+
async query(params) {
|
|
1863
|
+
console.error(`Executing GraphQL query against Supabase docs...`);
|
|
1864
|
+
console.error(`Query string: ${params.query}`);
|
|
1865
|
+
const requestBody = {
|
|
1866
|
+
query: params.query
|
|
1867
|
+
};
|
|
1868
|
+
console.error(`Request body: ${JSON.stringify(requestBody)}`);
|
|
1869
|
+
try {
|
|
1870
|
+
const response = await fetch(this.graphqlEndpoint, {
|
|
1871
|
+
method: "POST",
|
|
1872
|
+
headers: {
|
|
1873
|
+
"Content-Type": "application/json",
|
|
1874
|
+
"Accept": "application/json"
|
|
1875
|
+
},
|
|
1876
|
+
body: JSON.stringify(requestBody)
|
|
1877
|
+
});
|
|
1878
|
+
if (!response.ok) {
|
|
1879
|
+
const errorText = await response.text();
|
|
1880
|
+
throw new Error(`GraphQL query failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
1881
|
+
}
|
|
1882
|
+
const result = await response.json();
|
|
1883
|
+
if (result.errors && Array.isArray(result.errors) && result.errors.length > 0) {
|
|
1884
|
+
const errorMessages = result.errors.map((err) => err.message).join(", ");
|
|
1885
|
+
throw new Error(`GraphQL errors: ${errorMessages}`);
|
|
1886
|
+
}
|
|
1887
|
+
console.error("GraphQL query executed successfully");
|
|
1888
|
+
return result;
|
|
1889
|
+
} catch (error) {
|
|
1890
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1891
|
+
console.error(`Error executing GraphQL query: ${errorMessage}`);
|
|
1892
|
+
throw new Error(`Failed to query Supabase documentation: ${errorMessage}`);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
};
|
|
1896
|
+
function createContentApiClient(baseUrl) {
|
|
1897
|
+
return new SupabaseDocsApiClient(baseUrl);
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
// src/tools/search_docs.ts
|
|
1901
|
+
var SearchDocsInputSchema = z24.object({
|
|
1902
|
+
graphql_query: z24.string().describe("GraphQL query string to search Supabase documentation")
|
|
1903
|
+
});
|
|
1904
|
+
var SearchDocsOutputSchema = z24.any().describe("GraphQL query results from Supabase documentation");
|
|
1905
|
+
var mcpInputSchema22 = {
|
|
1906
|
+
type: "object",
|
|
1907
|
+
properties: {
|
|
1908
|
+
graphql_query: {
|
|
1909
|
+
type: "string",
|
|
1910
|
+
description: "GraphQL query string to search Supabase documentation. Must be a valid GraphQL query."
|
|
1911
|
+
}
|
|
1912
|
+
},
|
|
1913
|
+
required: ["graphql_query"]
|
|
1914
|
+
};
|
|
1915
|
+
var searchDocsTool = {
|
|
1916
|
+
name: "search_docs",
|
|
1917
|
+
description: `Search the Supabase official documentation using GraphQL. Must be a valid GraphQL query.
|
|
1918
|
+
You should default to calling this even if you think you already know the answer, since the documentation is always being updated.
|
|
1919
|
+
This tool queries the official Supabase documentation to help users understand Supabase features, APIs, and best practices.`,
|
|
1920
|
+
inputSchema: SearchDocsInputSchema,
|
|
1921
|
+
mcpInputSchema: mcpInputSchema22,
|
|
1922
|
+
outputSchema: SearchDocsOutputSchema,
|
|
1923
|
+
execute: async (input, context) => {
|
|
1924
|
+
context.log(`Raw input type: ${typeof input}`, "info");
|
|
1925
|
+
context.log(`Raw input: ${JSON.stringify(input)}`, "info");
|
|
1926
|
+
const { graphql_query } = input;
|
|
1927
|
+
context.log("Searching Supabase documentation...", "info");
|
|
1928
|
+
context.log(`GraphQL query type: ${typeof graphql_query}`, "info");
|
|
1929
|
+
context.log(`GraphQL query: ${graphql_query.substring(0, 100)}...`, "info");
|
|
1930
|
+
try {
|
|
1931
|
+
const contentApiClient = createContentApiClient();
|
|
1932
|
+
const result = await contentApiClient.query({ query: graphql_query });
|
|
1933
|
+
context.log("Documentation search completed successfully", "info");
|
|
1934
|
+
return result;
|
|
1935
|
+
} catch (error) {
|
|
1936
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1937
|
+
context.log(`Error searching documentation: ${errorMessage}`, "error");
|
|
1938
|
+
throw new Error(`Failed to search Supabase documentation: ${errorMessage}`);
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
};
|
|
1942
|
+
|
|
1715
1943
|
// src/index.ts
|
|
1716
1944
|
import * as fs from "node:fs";
|
|
1717
1945
|
import * as path from "node:path";
|
|
@@ -1829,16 +2057,16 @@ async function createRagAgentClient(config) {
|
|
|
1829
2057
|
}
|
|
1830
2058
|
|
|
1831
2059
|
// src/integrations/rag-agent-tools.ts
|
|
1832
|
-
import { z as
|
|
2060
|
+
import { z as z25 } from "zod";
|
|
1833
2061
|
function wrapRagAgentTool(ragTool, ragClient) {
|
|
1834
|
-
const inputSchema2 =
|
|
2062
|
+
const inputSchema2 = z25.any();
|
|
1835
2063
|
return {
|
|
1836
2064
|
name: `rag_${ragTool.name}`,
|
|
1837
2065
|
// Prefix with 'rag_' to avoid naming conflicts
|
|
1838
2066
|
description: ragTool.description || `RAG Agent tool: ${ragTool.name}`,
|
|
1839
2067
|
inputSchema: inputSchema2,
|
|
1840
2068
|
mcpInputSchema: ragTool.inputSchema,
|
|
1841
|
-
outputSchema:
|
|
2069
|
+
outputSchema: z25.any(),
|
|
1842
2070
|
execute: async (input, context) => {
|
|
1843
2071
|
context.log(`Calling RAG Agent tool: ${ragTool.name}`, "info");
|
|
1844
2072
|
try {
|
|
@@ -1862,48 +2090,464 @@ function wrapAllRagAgentTools(ragClient) {
|
|
|
1862
2090
|
return wrappedTools;
|
|
1863
2091
|
}
|
|
1864
2092
|
|
|
2093
|
+
// src/aliyun/client.ts
|
|
2094
|
+
import RdsAiPkg from "@alicloud/rdsai20250507/dist/client.js";
|
|
2095
|
+
import * as $RdsAi from "@alicloud/rdsai20250507";
|
|
2096
|
+
var RdsAiClient = RdsAiPkg.default || RdsAiPkg;
|
|
2097
|
+
var AliyunRdsAiClient = class {
|
|
2098
|
+
client;
|
|
2099
|
+
// RdsAi Client instance
|
|
2100
|
+
config;
|
|
2101
|
+
constructor(config) {
|
|
2102
|
+
this.config = config;
|
|
2103
|
+
const openApiConfig = {
|
|
2104
|
+
accessKeyId: config.accessKeyId,
|
|
2105
|
+
accessKeySecret: config.accessKeySecret,
|
|
2106
|
+
endpoint: config.endpoint || "rdsai.aliyuncs.com"
|
|
2107
|
+
};
|
|
2108
|
+
this.client = new RdsAiClient(openApiConfig);
|
|
2109
|
+
}
|
|
2110
|
+
/**
|
|
2111
|
+
* List all Supabase instances
|
|
2112
|
+
*/
|
|
2113
|
+
async listSupabaseInstances(options) {
|
|
2114
|
+
const request = new $RdsAi.DescribeAppInstancesRequest({
|
|
2115
|
+
appType: "supabase",
|
|
2116
|
+
regionId: options?.regionId || this.config.regionId,
|
|
2117
|
+
DBInstanceName: options?.dbInstanceName,
|
|
2118
|
+
pageSize: options?.pageSize || 50,
|
|
2119
|
+
pageNumber: options?.pageNumber || 1
|
|
2120
|
+
});
|
|
2121
|
+
try {
|
|
2122
|
+
const response = await this.client.describeAppInstances(request);
|
|
2123
|
+
if (!response.body) {
|
|
2124
|
+
throw new Error("Empty response from DescribeAppInstances API");
|
|
2125
|
+
}
|
|
2126
|
+
const instances = (response.body.instances || []).map((inst) => ({
|
|
2127
|
+
instanceName: inst.instanceName || inst.instance_name || "",
|
|
2128
|
+
appType: inst.appType || inst.app_type || "supabase",
|
|
2129
|
+
instanceMinorVersion: inst.instanceMinorVersion || inst.instance_minor_version || 0,
|
|
2130
|
+
vSwitchId: inst.VSwitchId || inst.v_switch_id || "",
|
|
2131
|
+
instanceClass: inst.instanceClass || inst.instance_class || "",
|
|
2132
|
+
status: inst.status || "",
|
|
2133
|
+
dbInstanceName: inst.DBInstanceName || inst.db_instance_name || "",
|
|
2134
|
+
regionId: inst.regionId || inst.region_id || "",
|
|
2135
|
+
appName: inst.appName || inst.app_name || "",
|
|
2136
|
+
publicConnectionString: inst.publicConnectionString || inst.public_connection_string || inst.publicUrl || inst.public_url || "",
|
|
2137
|
+
vpcConnectionString: inst.vpcConnectionString || inst.vpc_connection_string || inst.vpcUrl || inst.vpc_url || ""
|
|
2138
|
+
}));
|
|
2139
|
+
return {
|
|
2140
|
+
requestId: response.body.requestId || "",
|
|
2141
|
+
totalCount: response.body.totalCount || 0,
|
|
2142
|
+
maxResults: response.body.maxResults || 0,
|
|
2143
|
+
pageNumber: response.body.pageNumber || 1,
|
|
2144
|
+
pageSize: response.body.pageSize || 0,
|
|
2145
|
+
instances
|
|
2146
|
+
};
|
|
2147
|
+
} catch (error) {
|
|
2148
|
+
console.error("Error calling DescribeAppInstances:", error);
|
|
2149
|
+
throw new Error(`Failed to list Supabase instances: ${error instanceof Error ? error.message : String(error)}`);
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
/**
|
|
2153
|
+
* Get authentication information for a specific instance
|
|
2154
|
+
*/
|
|
2155
|
+
async getInstanceAuthInfo(instanceName) {
|
|
2156
|
+
const request = new $RdsAi.DescribeInstanceAuthInfoRequest({
|
|
2157
|
+
instanceName
|
|
2158
|
+
});
|
|
2159
|
+
try {
|
|
2160
|
+
const response = await this.client.describeInstanceAuthInfo(request);
|
|
2161
|
+
if (!response.body) {
|
|
2162
|
+
throw new Error("Empty response from DescribeInstanceAuthInfo API");
|
|
2163
|
+
}
|
|
2164
|
+
return {
|
|
2165
|
+
requestId: response.body.requestId || "",
|
|
2166
|
+
instanceName: response.body.instanceName || "",
|
|
2167
|
+
jwtSecret: response.body.jwtSecret || "",
|
|
2168
|
+
apiKeys: {
|
|
2169
|
+
anonKey: response.body.apiKeys?.anonKey || "",
|
|
2170
|
+
serviceKey: response.body.apiKeys?.serviceKey || ""
|
|
2171
|
+
},
|
|
2172
|
+
configList: (response.body.configList || []).map((item) => ({
|
|
2173
|
+
name: item.name || "",
|
|
2174
|
+
value: item.value || ""
|
|
2175
|
+
}))
|
|
2176
|
+
};
|
|
2177
|
+
} catch (error) {
|
|
2178
|
+
console.error("Error calling DescribeInstanceAuthInfo:", error);
|
|
2179
|
+
throw new Error(`Failed to get instance auth info: ${error instanceof Error ? error.message : String(error)}`);
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
/**
|
|
2183
|
+
* Get complete credentials for a Supabase instance
|
|
2184
|
+
*/
|
|
2185
|
+
async getSupabaseCredentials(instanceName, useVpc = false, regionId) {
|
|
2186
|
+
const queryRegion = regionId || this.config.regionId;
|
|
2187
|
+
const instancesResponse = await this.listSupabaseInstances({
|
|
2188
|
+
regionId: queryRegion
|
|
2189
|
+
});
|
|
2190
|
+
const instance = instancesResponse.instances.find((inst) => inst.instanceName === instanceName);
|
|
2191
|
+
if (!instance) {
|
|
2192
|
+
throw new Error(`Instance ${instanceName} not found. Available instances: ${instancesResponse.instances.map((i) => i.instanceName).join(", ")}`);
|
|
2193
|
+
}
|
|
2194
|
+
const authInfo = await this.getInstanceAuthInfo(instanceName);
|
|
2195
|
+
const connectionString = useVpc ? instance.vpcConnectionString : instance.publicConnectionString;
|
|
2196
|
+
const supabaseUrl = connectionString.startsWith("http") ? connectionString : `http://${connectionString}`;
|
|
2197
|
+
return {
|
|
2198
|
+
instanceName,
|
|
2199
|
+
supabaseUrl,
|
|
2200
|
+
anonKey: authInfo.apiKeys.anonKey,
|
|
2201
|
+
serviceKey: authInfo.apiKeys.serviceKey,
|
|
2202
|
+
jwtSecret: authInfo.jwtSecret,
|
|
2203
|
+
useVpc
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
};
|
|
2207
|
+
function createAliyunClient(config) {
|
|
2208
|
+
return new AliyunRdsAiClient(config);
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
// src/tools/aliyun/list_instances.ts
|
|
2212
|
+
import { z as z26 } from "zod";
|
|
2213
|
+
import { zodToJsonSchema as zodToJsonSchema2 } from "zod-to-json-schema";
|
|
2214
|
+
var ListInstancesInputZodSchema = z26.object({
|
|
2215
|
+
region_id: z26.string().optional().describe("Region ID to filter instances (e.g., cn-beijing)"),
|
|
2216
|
+
db_instance_name: z26.string().optional().describe("RDS PostgreSQL database instance ID to filter"),
|
|
2217
|
+
page_size: z26.number().int().min(1).max(50).optional().default(50).describe("Number of results per page (1-50)"),
|
|
2218
|
+
page_number: z26.number().int().min(1).optional().default(1).describe("Page number")
|
|
2219
|
+
});
|
|
2220
|
+
var InstanceInfoZodSchema = z26.object({
|
|
2221
|
+
instance_name: z26.string(),
|
|
2222
|
+
app_name: z26.string(),
|
|
2223
|
+
status: z26.string(),
|
|
2224
|
+
instance_class: z26.string(),
|
|
2225
|
+
region_id: z26.string(),
|
|
2226
|
+
public_url: z26.string(),
|
|
2227
|
+
vpc_url: z26.string(),
|
|
2228
|
+
db_instance_name: z26.string()
|
|
2229
|
+
});
|
|
2230
|
+
var ListInstancesOutputZodSchema = z26.object({
|
|
2231
|
+
success: z26.boolean(),
|
|
2232
|
+
total_count: z26.number(),
|
|
2233
|
+
page_number: z26.number(),
|
|
2234
|
+
page_size: z26.number(),
|
|
2235
|
+
instances: z26.array(InstanceInfoZodSchema)
|
|
2236
|
+
});
|
|
2237
|
+
async function execute2(input, aliyunClient, log) {
|
|
2238
|
+
if (!aliyunClient) {
|
|
2239
|
+
throw new Error("Alibaba Cloud client is not initialized. Please provide --aliyun-ak and --aliyun-sk parameters.");
|
|
2240
|
+
}
|
|
2241
|
+
log("Fetching Supabase instances from Alibaba Cloud...", "info");
|
|
2242
|
+
try {
|
|
2243
|
+
const response = await aliyunClient.listSupabaseInstances({
|
|
2244
|
+
regionId: input.region_id,
|
|
2245
|
+
dbInstanceName: input.db_instance_name,
|
|
2246
|
+
pageSize: input.page_size,
|
|
2247
|
+
pageNumber: input.page_number
|
|
2248
|
+
});
|
|
2249
|
+
const instances = response.instances.map((inst) => ({
|
|
2250
|
+
instance_name: inst.instanceName,
|
|
2251
|
+
app_name: inst.appName,
|
|
2252
|
+
status: inst.status,
|
|
2253
|
+
instance_class: inst.instanceClass,
|
|
2254
|
+
region_id: inst.regionId,
|
|
2255
|
+
public_url: inst.publicConnectionString.startsWith("http") ? inst.publicConnectionString : `http://${inst.publicConnectionString}`,
|
|
2256
|
+
vpc_url: inst.vpcConnectionString.startsWith("http") ? inst.vpcConnectionString : `http://${inst.vpcConnectionString}`,
|
|
2257
|
+
db_instance_name: inst.dbInstanceName
|
|
2258
|
+
}));
|
|
2259
|
+
log(`Found ${instances.length} Supabase instance(s)`, "info");
|
|
2260
|
+
return {
|
|
2261
|
+
success: true,
|
|
2262
|
+
total_count: response.totalCount,
|
|
2263
|
+
page_number: response.pageNumber,
|
|
2264
|
+
page_size: response.pageSize,
|
|
2265
|
+
instances
|
|
2266
|
+
};
|
|
2267
|
+
} catch (error) {
|
|
2268
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2269
|
+
log(`Error listing instances: ${errorMessage}`, "error");
|
|
2270
|
+
throw error;
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
var listAliyunSupabaseInstancesTool = {
|
|
2274
|
+
name: "list_aliyun_supabase_instances",
|
|
2275
|
+
description: "List all Supabase instances from Alibaba Cloud RDS AI. Returns instance names, status, connection URLs, and other metadata. Use this tool to discover available Supabase instances before connecting.",
|
|
2276
|
+
inputSchema: ListInstancesInputZodSchema,
|
|
2277
|
+
mcpInputSchema: zodToJsonSchema2(ListInstancesInputZodSchema),
|
|
2278
|
+
outputSchema: ListInstancesOutputZodSchema,
|
|
2279
|
+
execute: execute2
|
|
2280
|
+
};
|
|
2281
|
+
var list_instances_default = listAliyunSupabaseInstancesTool;
|
|
2282
|
+
|
|
2283
|
+
// src/tools/aliyun/connect_instance.ts
|
|
2284
|
+
import { z as z27 } from "zod";
|
|
2285
|
+
import { zodToJsonSchema as zodToJsonSchema3 } from "zod-to-json-schema";
|
|
2286
|
+
var ConnectInstanceInputZodSchema = z27.object({
|
|
2287
|
+
instance_name: z27.string().describe("The instance name (e.g., ra-supabase-8moov5lxba****)"),
|
|
2288
|
+
use_vpc: z27.boolean().optional().default(false).describe("Whether to use VPC connection instead of public connection")
|
|
2289
|
+
});
|
|
2290
|
+
var ConnectInstanceOutputZodSchema = z27.object({
|
|
2291
|
+
success: z27.boolean(),
|
|
2292
|
+
message: z27.string(),
|
|
2293
|
+
instance_name: z27.string(),
|
|
2294
|
+
supabase_url: z27.string(),
|
|
2295
|
+
connection_type: z27.string()
|
|
2296
|
+
});
|
|
2297
|
+
async function execute3(input, aliyunClient, createSupabaseClient, setCurrentInstance, log) {
|
|
2298
|
+
if (!aliyunClient) {
|
|
2299
|
+
throw new Error("Alibaba Cloud client is not initialized. Please provide --aliyun-ak and --aliyun-sk parameters.");
|
|
2300
|
+
}
|
|
2301
|
+
log(`Connecting to Supabase instance: ${input.instance_name}...`, "info");
|
|
2302
|
+
try {
|
|
2303
|
+
const credentials = await aliyunClient.getSupabaseCredentials(input.instance_name, input.use_vpc);
|
|
2304
|
+
log(`Retrieved credentials for instance ${input.instance_name}`, "info");
|
|
2305
|
+
log(`Supabase URL: ${credentials.supabaseUrl}`, "info");
|
|
2306
|
+
const supabaseClient = await createSupabaseClient(
|
|
2307
|
+
credentials.supabaseUrl,
|
|
2308
|
+
credentials.anonKey,
|
|
2309
|
+
credentials.serviceKey,
|
|
2310
|
+
credentials.jwtSecret
|
|
2311
|
+
);
|
|
2312
|
+
setCurrentInstance(input.instance_name, supabaseClient);
|
|
2313
|
+
log(`Successfully connected to instance ${input.instance_name}`, "info");
|
|
2314
|
+
return {
|
|
2315
|
+
success: true,
|
|
2316
|
+
message: `Successfully connected to Supabase instance ${input.instance_name}`,
|
|
2317
|
+
instance_name: input.instance_name,
|
|
2318
|
+
supabase_url: credentials.supabaseUrl,
|
|
2319
|
+
connection_type: input.use_vpc ? "VPC" : "Public"
|
|
2320
|
+
};
|
|
2321
|
+
} catch (error) {
|
|
2322
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2323
|
+
log(`Error connecting to instance: ${errorMessage}`, "error");
|
|
2324
|
+
throw new Error(`Failed to connect to instance ${input.instance_name}: ${errorMessage}`);
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
var connectToSupabaseInstanceTool = {
|
|
2328
|
+
name: "connect_to_supabase_instance",
|
|
2329
|
+
description: "Connect to a specific Supabase instance by instance name. This tool fetches credentials from Alibaba Cloud and establishes a connection. After connecting, you can use all Supabase tools (list_tables, execute_sql, etc.).",
|
|
2330
|
+
inputSchema: ConnectInstanceInputZodSchema,
|
|
2331
|
+
mcpInputSchema: zodToJsonSchema3(ConnectInstanceInputZodSchema),
|
|
2332
|
+
outputSchema: ConnectInstanceOutputZodSchema,
|
|
2333
|
+
execute: execute3
|
|
2334
|
+
};
|
|
2335
|
+
var connect_instance_default = connectToSupabaseInstanceTool;
|
|
2336
|
+
|
|
2337
|
+
// src/tools/aliyun/get_current_instance.ts
|
|
2338
|
+
import { z as z28 } from "zod";
|
|
2339
|
+
import { zodToJsonSchema as zodToJsonSchema4 } from "zod-to-json-schema";
|
|
2340
|
+
var GetCurrentInstanceInputZodSchema = z28.object({});
|
|
2341
|
+
var GetCurrentInstanceOutputZodSchema = z28.object({
|
|
2342
|
+
connected: z28.boolean(),
|
|
2343
|
+
instance_name: z28.string().optional(),
|
|
2344
|
+
message: z28.string()
|
|
2345
|
+
});
|
|
2346
|
+
async function execute4(input, getCurrentInstanceName, log) {
|
|
2347
|
+
const currentInstance = getCurrentInstanceName();
|
|
2348
|
+
if (currentInstance) {
|
|
2349
|
+
log(`Currently connected to instance: ${currentInstance}`, "info");
|
|
2350
|
+
return {
|
|
2351
|
+
connected: true,
|
|
2352
|
+
instance_name: currentInstance,
|
|
2353
|
+
message: `Connected to Supabase instance: ${currentInstance}`
|
|
2354
|
+
};
|
|
2355
|
+
} else {
|
|
2356
|
+
log("Not connected to any Supabase instance", "info");
|
|
2357
|
+
return {
|
|
2358
|
+
connected: false,
|
|
2359
|
+
message: "Not connected to any Supabase instance. Use connect_to_supabase_instance to connect."
|
|
2360
|
+
};
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
var getCurrentSupabaseInstanceTool = {
|
|
2364
|
+
name: "get_current_supabase_instance",
|
|
2365
|
+
description: "Get information about the currently connected Supabase instance. Returns the instance name if connected, or a message indicating no connection.",
|
|
2366
|
+
inputSchema: GetCurrentInstanceInputZodSchema,
|
|
2367
|
+
mcpInputSchema: zodToJsonSchema4(GetCurrentInstanceInputZodSchema),
|
|
2368
|
+
outputSchema: GetCurrentInstanceOutputZodSchema,
|
|
2369
|
+
execute: execute4
|
|
2370
|
+
};
|
|
2371
|
+
var get_current_instance_default = getCurrentSupabaseInstanceTool;
|
|
2372
|
+
|
|
2373
|
+
// src/tools/aliyun/disconnect_instance.ts
|
|
2374
|
+
import { z as z29 } from "zod";
|
|
2375
|
+
import { zodToJsonSchema as zodToJsonSchema5 } from "zod-to-json-schema";
|
|
2376
|
+
var DisconnectInstanceInputZodSchema = z29.object({});
|
|
2377
|
+
var DisconnectInstanceOutputZodSchema = z29.object({
|
|
2378
|
+
success: z29.boolean(),
|
|
2379
|
+
message: z29.string(),
|
|
2380
|
+
previous_instance: z29.string().optional()
|
|
2381
|
+
});
|
|
2382
|
+
async function execute5(input, getCurrentInstanceName, clearCurrentInstance, log) {
|
|
2383
|
+
const currentInstance = getCurrentInstanceName();
|
|
2384
|
+
if (!currentInstance) {
|
|
2385
|
+
log("No active connection to disconnect", "info");
|
|
2386
|
+
return {
|
|
2387
|
+
success: false,
|
|
2388
|
+
message: "Not connected to any Supabase instance"
|
|
2389
|
+
};
|
|
2390
|
+
}
|
|
2391
|
+
log(`Disconnecting from instance: ${currentInstance}`, "info");
|
|
2392
|
+
clearCurrentInstance();
|
|
2393
|
+
log("Disconnected successfully", "info");
|
|
2394
|
+
return {
|
|
2395
|
+
success: true,
|
|
2396
|
+
message: `Successfully disconnected from instance ${currentInstance}`,
|
|
2397
|
+
previous_instance: currentInstance
|
|
2398
|
+
};
|
|
2399
|
+
}
|
|
2400
|
+
var disconnectSupabaseInstanceTool = {
|
|
2401
|
+
name: "disconnect_supabase_instance",
|
|
2402
|
+
description: "Disconnect from the currently connected Supabase instance. After disconnecting, you need to connect to an instance again before using Supabase tools.",
|
|
2403
|
+
inputSchema: DisconnectInstanceInputZodSchema,
|
|
2404
|
+
mcpInputSchema: zodToJsonSchema5(DisconnectInstanceInputZodSchema),
|
|
2405
|
+
outputSchema: DisconnectInstanceOutputZodSchema,
|
|
2406
|
+
execute: execute5
|
|
2407
|
+
};
|
|
2408
|
+
var disconnect_instance_default = disconnectSupabaseInstanceTool;
|
|
2409
|
+
|
|
1865
2410
|
// src/index.ts
|
|
1866
2411
|
async function main() {
|
|
1867
2412
|
const program = new Command();
|
|
1868
|
-
program.name("self-hosted-supabase-mcp").description("MCP Server for self-hosted Supabase instances").option("--url <url>", "Supabase project URL", process.env.SUPABASE_URL).option("--anon-key <key>", "Supabase anonymous key", process.env.SUPABASE_ANON_KEY).option("--service-key <key>", "Supabase service role key (optional)", process.env.SUPABASE_SERVICE_ROLE_KEY).option("--db-url <url>", "Direct database connection string (optional, for pg fallback)", process.env.DATABASE_URL).option("--jwt-secret <secret>", "Supabase JWT secret (optional,
|
|
2413
|
+
program.name("self-hosted-supabase-mcp").description("MCP Server for self-hosted Supabase instances").option("--url <url>", "Supabase project URL (legacy mode)", process.env.SUPABASE_URL).option("--anon-key <key>", "Supabase anonymous key (legacy mode)", process.env.SUPABASE_ANON_KEY).option("--service-key <key>", "Supabase service role key (legacy mode, optional)", process.env.SUPABASE_SERVICE_ROLE_KEY).option("--db-url <url>", "Direct database connection string (optional, for pg fallback)", process.env.DATABASE_URL).option("--jwt-secret <secret>", "Supabase JWT secret (legacy mode, optional)", process.env.SUPABASE_AUTH_JWT_SECRET).option("--aliyun-ak <key>", "Alibaba Cloud Access Key ID", process.env.ALIYUN_ACCESS_KEY_ID).option("--aliyun-sk <secret>", "Alibaba Cloud Access Key Secret", process.env.ALIYUN_ACCESS_KEY_SECRET).option("--aliyun-region <region>", "Alibaba Cloud region (optional)", process.env.ALIYUN_REGION).option("--workspace-path <path>", "Workspace root path (for file operations)", process.cwd()).option("--tools-config <path>", 'Path to a JSON file specifying which tools to enable (e.g., { "enabledTools": ["tool1", "tool2"] }). If omitted, all tools are enabled.').option("--enable-rag-agent", "Enable RAG Agent MCP integration (uses --url host:port and --anon-key as API key)", process.env.ENABLE_RAG_AGENT === "true").parse(process.argv);
|
|
1869
2414
|
const options = program.opts();
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
2415
|
+
const isAliyunMode = !!(options.aliyunAk && options.aliyunSk);
|
|
2416
|
+
const isLegacyMode = !!(options.url && options.anonKey);
|
|
2417
|
+
if (!isAliyunMode && !isLegacyMode) {
|
|
2418
|
+
console.error("Error: Either Alibaba Cloud credentials (--aliyun-ak, --aliyun-sk) or legacy Supabase credentials (--url, --anon-key) are required.");
|
|
2419
|
+
throw new Error("Missing required credentials.");
|
|
1873
2420
|
}
|
|
1874
|
-
if (
|
|
1875
|
-
console.error("
|
|
1876
|
-
throw new Error("Supabase Anon Key is required.");
|
|
2421
|
+
if (isAliyunMode && isLegacyMode) {
|
|
2422
|
+
console.error("Warning: Both Alibaba Cloud and legacy credentials provided. Using Alibaba Cloud mode.");
|
|
1877
2423
|
}
|
|
1878
2424
|
console.error("Initializing Self-Hosted Supabase MCP Server...");
|
|
1879
2425
|
try {
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
2426
|
+
let aliyunClient = null;
|
|
2427
|
+
if (isAliyunMode) {
|
|
2428
|
+
console.error("Alibaba Cloud mode enabled, initializing client...");
|
|
2429
|
+
aliyunClient = createAliyunClient({
|
|
2430
|
+
accessKeyId: options.aliyunAk,
|
|
2431
|
+
accessKeySecret: options.aliyunSk,
|
|
2432
|
+
regionId: options.aliyunRegion
|
|
2433
|
+
});
|
|
2434
|
+
console.error("Alibaba Cloud client initialized successfully.");
|
|
2435
|
+
}
|
|
2436
|
+
let selfhostedClient = null;
|
|
2437
|
+
if (isLegacyMode && !isAliyunMode) {
|
|
2438
|
+
selfhostedClient = await SelfhostedSupabaseClient.create({
|
|
2439
|
+
supabaseUrl: options.url,
|
|
2440
|
+
supabaseAnonKey: options.anonKey,
|
|
2441
|
+
supabaseServiceRoleKey: options.serviceKey,
|
|
2442
|
+
databaseUrl: options.dbUrl,
|
|
2443
|
+
jwtSecret: options.jwtSecret
|
|
2444
|
+
});
|
|
2445
|
+
console.error("Supabase client initialized successfully (legacy mode).");
|
|
2446
|
+
}
|
|
2447
|
+
let currentInstanceName = null;
|
|
2448
|
+
let currentSupabaseClient = selfhostedClient;
|
|
2449
|
+
const getCurrentInstanceName = () => currentInstanceName;
|
|
2450
|
+
const setCurrentInstance = (instanceName, client) => {
|
|
2451
|
+
currentInstanceName = instanceName;
|
|
2452
|
+
currentSupabaseClient = client;
|
|
2453
|
+
};
|
|
2454
|
+
const clearCurrentInstance = () => {
|
|
2455
|
+
currentInstanceName = null;
|
|
2456
|
+
currentSupabaseClient = null;
|
|
2457
|
+
};
|
|
2458
|
+
const createSupabaseClientDynamic = async (url, anonKey, serviceKey, jwtSecret) => {
|
|
2459
|
+
return await SelfhostedSupabaseClient.create({
|
|
2460
|
+
supabaseUrl: url,
|
|
2461
|
+
supabaseAnonKey: anonKey,
|
|
2462
|
+
supabaseServiceRoleKey: serviceKey,
|
|
2463
|
+
databaseUrl: options.dbUrl,
|
|
2464
|
+
jwtSecret
|
|
2465
|
+
});
|
|
2466
|
+
};
|
|
1888
2467
|
let ragAgentClient = null;
|
|
1889
|
-
|
|
2468
|
+
const initializeRagAgent = async (supabaseClient) => {
|
|
2469
|
+
if (!options.enableRagAgent) {
|
|
2470
|
+
return null;
|
|
2471
|
+
}
|
|
1890
2472
|
try {
|
|
1891
|
-
console.error("RAG Agent
|
|
1892
|
-
const
|
|
2473
|
+
console.error("Initializing RAG Agent client...");
|
|
2474
|
+
const urlToUse = supabaseClient.getSupabaseUrl();
|
|
2475
|
+
const anonKeyToUse = supabaseClient.getAnonKey();
|
|
2476
|
+
const urlObj = new URL(urlToUse);
|
|
1893
2477
|
const host = urlObj.hostname;
|
|
1894
2478
|
const port = urlObj.port ? parseInt(urlObj.port, 10) : urlObj.protocol === "https:" ? 443 : 80;
|
|
1895
|
-
|
|
2479
|
+
const client = await createRagAgentClient({
|
|
1896
2480
|
host,
|
|
1897
2481
|
port,
|
|
1898
|
-
apiKey:
|
|
2482
|
+
apiKey: anonKeyToUse
|
|
1899
2483
|
});
|
|
1900
2484
|
console.error("RAG Agent client initialized successfully.");
|
|
2485
|
+
return client;
|
|
1901
2486
|
} catch (error) {
|
|
1902
2487
|
console.error("Failed to initialize RAG Agent client:", error);
|
|
1903
2488
|
console.error("Continuing without RAG Agent integration...");
|
|
2489
|
+
return null;
|
|
1904
2490
|
}
|
|
2491
|
+
};
|
|
2492
|
+
if (options.enableRagAgent && isLegacyMode && selfhostedClient) {
|
|
2493
|
+
ragAgentClient = await initializeRagAgent(selfhostedClient);
|
|
2494
|
+
} else if (options.enableRagAgent && isAliyunMode) {
|
|
2495
|
+
console.error("RAG Agent integration enabled.");
|
|
2496
|
+
console.error("RAG Agent tools will be available after connecting to a Supabase instance.");
|
|
2497
|
+
}
|
|
2498
|
+
const wrappedAliyunTools = {};
|
|
2499
|
+
if (isAliyunMode) {
|
|
2500
|
+
wrappedAliyunTools[list_instances_default.name] = {
|
|
2501
|
+
...list_instances_default,
|
|
2502
|
+
execute: async (input, context) => {
|
|
2503
|
+
return await list_instances_default.execute(
|
|
2504
|
+
input,
|
|
2505
|
+
aliyunClient,
|
|
2506
|
+
context.log
|
|
2507
|
+
);
|
|
2508
|
+
}
|
|
2509
|
+
};
|
|
2510
|
+
wrappedAliyunTools[connect_instance_default.name] = {
|
|
2511
|
+
...connect_instance_default,
|
|
2512
|
+
execute: async (input, context) => {
|
|
2513
|
+
const result = await connect_instance_default.execute(
|
|
2514
|
+
input,
|
|
2515
|
+
aliyunClient,
|
|
2516
|
+
createSupabaseClientDynamic,
|
|
2517
|
+
setCurrentInstance,
|
|
2518
|
+
context.log
|
|
2519
|
+
);
|
|
2520
|
+
if (result.success && currentSupabaseClient && options.enableRagAgent && !ragAgentClient) {
|
|
2521
|
+
ragAgentClient = await initializeRagAgent(currentSupabaseClient);
|
|
2522
|
+
}
|
|
2523
|
+
return result;
|
|
2524
|
+
}
|
|
2525
|
+
};
|
|
2526
|
+
wrappedAliyunTools[get_current_instance_default.name] = {
|
|
2527
|
+
...get_current_instance_default,
|
|
2528
|
+
execute: async (input, context) => {
|
|
2529
|
+
return await get_current_instance_default.execute(
|
|
2530
|
+
input,
|
|
2531
|
+
getCurrentInstanceName,
|
|
2532
|
+
context.log
|
|
2533
|
+
);
|
|
2534
|
+
}
|
|
2535
|
+
};
|
|
2536
|
+
wrappedAliyunTools[disconnect_instance_default.name] = {
|
|
2537
|
+
...disconnect_instance_default,
|
|
2538
|
+
execute: async (input, context) => {
|
|
2539
|
+
return await disconnect_instance_default.execute(
|
|
2540
|
+
input,
|
|
2541
|
+
getCurrentInstanceName,
|
|
2542
|
+
clearCurrentInstance,
|
|
2543
|
+
context.log
|
|
2544
|
+
);
|
|
2545
|
+
}
|
|
2546
|
+
};
|
|
1905
2547
|
}
|
|
1906
2548
|
const availableTools = {
|
|
2549
|
+
// Alibaba Cloud tools (if in Aliyun mode)
|
|
2550
|
+
...wrappedAliyunTools,
|
|
1907
2551
|
// Cast here assumes tools will implement AppTool structure
|
|
1908
2552
|
[listTablesTool.name]: listTablesTool,
|
|
1909
2553
|
[listExtensionsTool.name]: listExtensionsTool,
|
|
@@ -1925,12 +2569,45 @@ async function main() {
|
|
|
1925
2569
|
[updateAuthUserTool.name]: updateAuthUserTool,
|
|
1926
2570
|
[list_storage_buckets_default.name]: list_storage_buckets_default,
|
|
1927
2571
|
[list_storage_objects_default.name]: list_storage_objects_default,
|
|
1928
|
-
[list_realtime_publications_default.name]: list_realtime_publications_default
|
|
2572
|
+
[list_realtime_publications_default.name]: list_realtime_publications_default,
|
|
2573
|
+
[install_execute_sql_function_default.name]: install_execute_sql_function_default,
|
|
2574
|
+
[searchDocsTool.name]: searchDocsTool
|
|
1929
2575
|
};
|
|
1930
|
-
if (
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
2576
|
+
if (options.enableRagAgent) {
|
|
2577
|
+
if (ragAgentClient) {
|
|
2578
|
+
const ragTools = wrapAllRagAgentTools(ragAgentClient);
|
|
2579
|
+
Object.assign(availableTools, ragTools);
|
|
2580
|
+
console.error(`Added ${Object.keys(ragTools).length} RAG Agent tools to available tools.`);
|
|
2581
|
+
} else if (isAliyunMode) {
|
|
2582
|
+
const dummyRagTools = await (async () => {
|
|
2583
|
+
try {
|
|
2584
|
+
const tempClient = await createRagAgentClient({
|
|
2585
|
+
host: "localhost",
|
|
2586
|
+
port: 80,
|
|
2587
|
+
apiKey: "dummy"
|
|
2588
|
+
});
|
|
2589
|
+
const tools = wrapAllRagAgentTools(tempClient);
|
|
2590
|
+
await tempClient.close();
|
|
2591
|
+
return tools;
|
|
2592
|
+
} catch (error) {
|
|
2593
|
+
console.error("Warning: Could not create RAG Agent tool schemas:", error);
|
|
2594
|
+
return {};
|
|
2595
|
+
}
|
|
2596
|
+
})();
|
|
2597
|
+
for (const [toolName, tool] of Object.entries(dummyRagTools)) {
|
|
2598
|
+
availableTools[toolName] = {
|
|
2599
|
+
...tool,
|
|
2600
|
+
execute: async (input, context) => {
|
|
2601
|
+
if (!ragAgentClient) {
|
|
2602
|
+
throw new Error("RAG Agent is not initialized. Please connect to a Supabase instance first using connect_to_supabase_instance tool.");
|
|
2603
|
+
}
|
|
2604
|
+
const ragTools = wrapAllRagAgentTools(ragAgentClient);
|
|
2605
|
+
return await ragTools[toolName].execute(input, context);
|
|
2606
|
+
}
|
|
2607
|
+
};
|
|
2608
|
+
}
|
|
2609
|
+
console.error(`Added ${Object.keys(dummyRagTools).length} RAG Agent tools (will be initialized after connection).`);
|
|
2610
|
+
}
|
|
1934
2611
|
}
|
|
1935
2612
|
let registeredTools = { ...availableTools };
|
|
1936
2613
|
const toolsConfigPath = options.toolsConfig;
|
|
@@ -2015,12 +2692,31 @@ async function main() {
|
|
|
2015
2692
|
if (typeof tool.execute !== "function") {
|
|
2016
2693
|
throw new Error(`Tool ${toolName} does not have an execute method.`);
|
|
2017
2694
|
}
|
|
2695
|
+
if (toolName === "search_docs") {
|
|
2696
|
+
console.error(`[DEBUG] request.params.arguments type: ${typeof request.params.arguments}`);
|
|
2697
|
+
console.error(`[DEBUG] request.params.arguments: ${JSON.stringify(request.params.arguments)}`);
|
|
2698
|
+
}
|
|
2018
2699
|
let parsedArgs = request.params.arguments;
|
|
2019
2700
|
if (tool.inputSchema && typeof tool.inputSchema.parse === "function") {
|
|
2020
2701
|
parsedArgs = tool.inputSchema.parse(request.params.arguments);
|
|
2702
|
+
if (toolName === "search_docs") {
|
|
2703
|
+
console.error(`[DEBUG] parsedArgs after Zod: ${JSON.stringify(parsedArgs)}`);
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
const isAliyunManagementTool = [
|
|
2707
|
+
"list_aliyun_supabase_instances",
|
|
2708
|
+
"connect_to_supabase_instance",
|
|
2709
|
+
"get_current_supabase_instance",
|
|
2710
|
+
"disconnect_supabase_instance"
|
|
2711
|
+
].includes(toolName);
|
|
2712
|
+
if (isAliyunMode && !isAliyunManagementTool && !currentSupabaseClient) {
|
|
2713
|
+
throw new McpError(
|
|
2714
|
+
ErrorCode.InvalidRequest,
|
|
2715
|
+
"Not connected to any Supabase instance. Please use connect_to_supabase_instance tool first."
|
|
2716
|
+
);
|
|
2021
2717
|
}
|
|
2022
2718
|
const context = {
|
|
2023
|
-
selfhostedClient,
|
|
2719
|
+
selfhostedClient: currentSupabaseClient,
|
|
2024
2720
|
workspacePath: options.workspacePath,
|
|
2025
2721
|
log: (message, level = "info") => {
|
|
2026
2722
|
console.error(`[${level.toUpperCase()}] ${message}`);
|
|
@@ -2038,7 +2734,7 @@ async function main() {
|
|
|
2038
2734
|
} catch (error) {
|
|
2039
2735
|
console.error(`Error executing tool ${toolName}:`, error);
|
|
2040
2736
|
let errorMessage = `Error executing tool ${toolName}: `;
|
|
2041
|
-
if (error instanceof
|
|
2737
|
+
if (error instanceof z30.ZodError) {
|
|
2042
2738
|
errorMessage += `Input validation failed: ${error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ")}`;
|
|
2043
2739
|
} else if (error instanceof Error) {
|
|
2044
2740
|
errorMessage += error.message;
|