@exposify/supabase 0.3.6 → 0.3.7

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 (4) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +45 -0
  3. package/dist/cli.js +169 -24
  4. package/package.json +2 -11
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Ali
3
+ Copyright (c) 2026 Exposify
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -110,6 +110,43 @@ note on introspection below) — without those, `init` can't reach anything to
110
110
  introspect and falls back to the fixed scaffold-and-ask-to-deploy flow above,
111
111
  same as before this wizard existed.
112
112
 
113
+ ### Using the hosted broker
114
+
115
+ If you use the Exposify hosted broker (the dashboard's "Hosted broker" card),
116
+ `init` asks first:
117
+
118
+ ```
119
+ Use the Exposify hosted broker for auth (MCP clients get a long-lived extb_... token instead of a Supabase JWT)? [y/N]
120
+ ```
121
+
122
+ Or skip the question with `--broker-url <url> --broker-project <id>`
123
+ (or `EXPOSIFY_BROKER_URL`/`EXPOSIFY_PROJECT_ID`), which also works
124
+ non-interactively. Saying yes does three things:
125
+
126
+ 1. **Wires the resolver into the function.** Adds
127
+ `auth: createHostedAuthResolver({ brokerUrl, projectId, licenseKey })` and
128
+ `authRequired: true` to `aiReady()`. The license key is read from the
129
+ `EXPOSIFY_LICENSE_KEY` secret and is never written into the file. If
130
+ `index.ts` already exists, `init` prints the snippet for you to merge in
131
+ yourself.
132
+ 2. **Sets `verify_jwt = false` for `mcp` in `supabase/config.toml`.** An
133
+ `extb_...` token isn't a JWT, so with the gateway's check on, every request
134
+ fails with `{"code":"UNAUTHORIZED_ASYMMETRIC_JWT","message":"Invalid JWT"}`
135
+ before the function runs. The setting goes in config.toml and not just
136
+ on the deploy command line, because a later plain
137
+ `supabase functions deploy mcp` would otherwise turn the check back on.
138
+ With the gateway check off, `authRequired: true` is what rejects requests
139
+ that have no valid broker token. Otherwise those requests would run as
140
+ `anon`.
141
+ 3. **Deploys with `--no-verify-jwt`**, and adds
142
+ `supabase secrets set EXPOSIFY_LICENSE_KEY=...` to the printed steps.
143
+ `init` never sees the license key, so you set that secret yourself.
144
+ Until you do, every request gets a 401.
145
+
146
+ MCP clients then send `Authorization: Bearer extb_...`. Don't give the client
147
+ the JWT that `POST /resolve` returns. It's the short-lived Supabase token the
148
+ function swaps in on each request, and it stops working when it expires.
149
+
113
150
  ## 2. Decide what's exposed
114
151
 
115
152
  **Every RLS-enabled table is automatically readable.** A table only shows up
@@ -253,6 +290,14 @@ Deno.serve(aiReady({ auth: broker.resolver, authRequired: true, writes: ["todos.
253
290
  failed refresh. By default the request then runs as the `anon` role (RLS
254
291
  decides). Set `authRequired: true` to answer `401` instead. Recommended.
255
292
  - A resolver that throws yields a 503.
293
+ - **Turn off the gateway's JWT check for the function**
294
+ (`[functions.mcp] verify_jwt = false` in `supabase/config.toml`, or
295
+ `supabase functions deploy mcp --no-verify-jwt`). Otherwise Supabase
296
+ rejects the `extb_...` token as `Invalid JWT` before your code runs. With
297
+ the check off, `authRequired: true` stops being optional.
298
+ - For the platform-hosted broker, use `createHostedAuthResolver` in place of
299
+ `broker.resolver`. `init` can set this up for you; see "Using the hosted
300
+ broker" above.
256
301
 
257
302
  **Deployment constraints** (not verified end to end, see HANDOVER):
258
303
 
package/dist/cli.js CHANGED
@@ -198,6 +198,7 @@ function buildCommentStatements(entries) {
198
198
  return lines.length > 0 ? `${lines.join("\n")}
199
199
  ` : "";
200
200
  }
201
+ var LICENSE_KEY_SECRET = "EXPOSIFY_LICENSE_KEY";
201
202
  var ANONYMOUS_SIGN_IN_WARNING = `// NOTE: this project has anonymous sign-in enabled. An anonymous Supabase
202
203
  // Auth session still gets role: authenticated (with an is_anonymous claim),
203
204
  // NOT role: anon -- so it passes any RLS policy scoped to "authenticated"
@@ -208,8 +209,20 @@ var ANONYMOUS_SIGN_IN_WARNING = `// NOTE: this project has anonymous sign-in ena
208
209
  function buildFunctionTemplate(opts = {}) {
209
210
  const lines = [];
210
211
  if (opts.anonymousSignIn) lines.push(ANONYMOUS_SIGN_IN_WARNING);
211
- lines.push(`import { aiReady } from "npm:@exposify/supabase";`, "");
212
+ lines.push(`import { aiReady } from "npm:@exposify/supabase";`);
213
+ if (opts.broker) lines.push(`import { createHostedAuthResolver } from "npm:@exposify/token-broker";`);
214
+ lines.push("");
212
215
  lines.push("Deno.serve(aiReady({");
216
+ if (opts.broker) {
217
+ lines.push(
218
+ " auth: createHostedAuthResolver({",
219
+ ` brokerUrl: ${JSON.stringify(opts.broker.brokerUrl)},`,
220
+ ` projectId: ${JSON.stringify(opts.broker.projectId)},`,
221
+ ` licenseKey: Deno.env.get(${JSON.stringify(LICENSE_KEY_SECRET)}) ?? "",`,
222
+ " }),",
223
+ " authRequired: true,"
224
+ );
225
+ }
213
226
  lines.push(
214
227
  opts.writes && opts.writes.length > 0 ? ` writes: ${JSON.stringify(opts.writes)},` : ` // writes: ["orders.insert", "orders.update"],`
215
228
  );
@@ -225,6 +238,50 @@ function buildFunctionTemplate(opts = {}) {
225
238
  );
226
239
  return lines.join("\n");
227
240
  }
241
+ function brokerUrlProblem(brokerUrl) {
242
+ let url;
243
+ try {
244
+ url = new URL(brokerUrl);
245
+ } catch {
246
+ return `"${brokerUrl}" is not a valid URL.`;
247
+ }
248
+ const local = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "[::1]";
249
+ if (url.protocol === "https:" || url.protocol === "http:" && local) return null;
250
+ return `broker URL must be https (http is only allowed for localhost): ${brokerUrl}`;
251
+ }
252
+ function normalizeBrokerUrl(brokerUrl) {
253
+ return brokerUrl.trim().replace(/\/+$/, "");
254
+ }
255
+ function setFunctionVerifyJwtFalse(toml, name = "mcp") {
256
+ const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
257
+ const header = new RegExp(`^\\s*\\[\\s*functions\\s*\\.\\s*("?)${escaped}\\1\\s*\\]\\s*(#.*)?$`);
258
+ const anyHeader = /^\s*\[/;
259
+ const verifyLine = /^(\s*)verify_jwt\s*=\s*([^#\s]*)(.*)$/;
260
+ const lines = toml.split("\n");
261
+ const start = lines.findIndex((l) => header.test(l));
262
+ if (start === -1) {
263
+ const sep = toml === "" || toml.endsWith("\n\n") ? "" : toml.endsWith("\n") ? "\n" : "\n\n";
264
+ return { toml: `${toml}${sep}[functions.${name}]
265
+ verify_jwt = false
266
+ `, changed: true };
267
+ }
268
+ let end = lines.length;
269
+ for (let i = start + 1; i < lines.length; i++) {
270
+ if (anyHeader.test(lines[i])) {
271
+ end = i;
272
+ break;
273
+ }
274
+ }
275
+ for (let i = start + 1; i < end; i++) {
276
+ const m = lines[i].match(verifyLine);
277
+ if (!m) continue;
278
+ if (m[2] === "false") return { toml, changed: false };
279
+ lines[i] = `${m[1]}verify_jwt = false${m[3]}`;
280
+ return { toml: lines.join("\n"), changed: true };
281
+ }
282
+ lines.splice(start + 1, 0, "verify_jwt = false");
283
+ return { toml: lines.join("\n"), changed: true };
284
+ }
228
285
  function findExistingMigration(filenames, suffix) {
229
286
  const matches = filenames.filter((f) => f.endsWith(suffix)).sort();
230
287
  return matches[0] ?? null;
@@ -249,6 +306,7 @@ function partialConnectionWarning(url, key) {
249
306
  var PACKAGE_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
250
307
  var FUNCTION_DIR = "supabase/functions/mcp";
251
308
  var MIGRATIONS_DIR = "supabase/migrations";
309
+ var CONFIG_TOML = "supabase/config.toml";
252
310
  var FETCH_TIMEOUT_MS = 1e4;
253
311
  function fetchWithTimeout(input, init) {
254
312
  return fetch(input, { ...init, signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
@@ -289,22 +347,39 @@ async function promptYesNo(rl, question, defaultYes = false) {
289
347
  async function promptText(rl, question) {
290
348
  return (await rl.question(question)).trim();
291
349
  }
350
+ function deployArgs(broker) {
351
+ return ["functions", "deploy", "mcp", ...broker ? ["--no-verify-jwt"] : []];
352
+ }
353
+ function clientCredential(broker) {
354
+ return broker ? "a broker token (extb_..., minted by the hosted broker)" : "a user JWT";
355
+ }
292
356
  function printManualCommands(opts = {}) {
357
+ const broker = opts.broker ?? null;
293
358
  console.log("\nRun these to finish setup:");
294
359
  const steps = [];
295
360
  if (!opts.alreadyPushed) steps.push("supabase db push");
296
- if (!opts.alreadyDeployed) steps.push("supabase functions deploy mcp");
361
+ if (broker) steps.push(`supabase secrets set ${LICENSE_KEY_SECRET}=<your license key (expfy_...)>`);
362
+ if (!opts.alreadyDeployed) steps.push(`supabase ${deployArgs(broker).join(" ")}`);
297
363
  steps.forEach((cmd, i) => console.log(` ${i + 1}. ${cmd}`));
298
- console.log(` ${steps.length + 1}. Paste the function URL + a user JWT into an MCP client.`);
364
+ console.log(` ${steps.length + 1}. Paste the function URL + ${clientCredential(broker)} into an MCP client.`);
365
+ }
366
+ function printLicenseKeyReminder(broker) {
367
+ if (!broker) return;
368
+ console.log(
369
+ `
370
+ If you haven't already: supabase secrets set ${LICENSE_KEY_SECRET}=<your license key (expfy_...)> -- until it's set, every request is rejected with 401.`
371
+ );
299
372
  }
300
- function pushAndDeploy() {
373
+ function pushAndDeploy(broker) {
301
374
  let pushed = false;
302
375
  try {
303
376
  console.log("\n$ supabase db push");
304
377
  execFileSync("supabase", ["db", "push"], { stdio: "inherit" });
305
378
  pushed = true;
306
- console.log("\n$ supabase functions deploy mcp");
307
- execFileSync("supabase", ["functions", "deploy", "mcp"], { stdio: "inherit" });
379
+ const args = deployArgs(broker);
380
+ console.log(`
381
+ $ supabase ${args.join(" ")}`);
382
+ execFileSync("supabase", args, { stdio: "inherit" });
308
383
  return { pushed: true, deployed: true };
309
384
  } catch (err) {
310
385
  return { pushed, deployed: false, error: err };
@@ -438,7 +513,53 @@ async function warnIfNotSupabaseProject() {
438
513
  );
439
514
  }
440
515
  }
516
+ function brokerFromFlags() {
517
+ const flagUrl = process.env.EXPOSIFY_BROKER_URL ?? argValue("--broker-url");
518
+ const flagProject = process.env.EXPOSIFY_PROJECT_ID ?? argValue("--broker-project");
519
+ if (!flagUrl && !flagProject) return void 0;
520
+ if (!flagUrl || !flagProject) {
521
+ throw new Error("the hosted broker needs both --broker-url and --broker-project (or EXPOSIFY_BROKER_URL/EXPOSIFY_PROJECT_ID).");
522
+ }
523
+ const problem = brokerUrlProblem(flagUrl);
524
+ if (problem) throw new Error(problem);
525
+ return { brokerUrl: normalizeBrokerUrl(flagUrl), projectId: flagProject.trim() };
526
+ }
527
+ async function resolveBroker(rl, fromFlags) {
528
+ if (fromFlags) return fromFlags;
529
+ if (!rl) return null;
530
+ const use = await promptYesNo(
531
+ rl,
532
+ "\nUse the Exposify hosted broker for auth (MCP clients get a long-lived extb_... token instead of a Supabase JWT)?"
533
+ );
534
+ if (!use) return null;
535
+ let brokerUrl = "";
536
+ for (; ; ) {
537
+ brokerUrl = normalizeBrokerUrl(await promptText(rl, " Broker URL (from the dashboard's Hosted broker card): "));
538
+ const problem = brokerUrlProblem(brokerUrl);
539
+ if (!problem) break;
540
+ console.log(` ${problem}`);
541
+ }
542
+ let projectId = "";
543
+ while (!projectId) projectId = await promptText(rl, " Project ID: ");
544
+ return { brokerUrl, projectId };
545
+ }
546
+ async function disableJwtVerification() {
547
+ if (!await exists(CONFIG_TOML)) {
548
+ console.warn(
549
+ `warn: no ${CONFIG_TOML} to record verify_jwt = false in -- always deploy with \`supabase functions deploy mcp --no-verify-jwt\`, or broker tokens will be rejected with "Invalid JWT".`
550
+ );
551
+ return;
552
+ }
553
+ const { toml, changed } = setFunctionVerifyJwtFalse(await readFile(CONFIG_TOML, "utf8"));
554
+ if (!changed) {
555
+ console.log(`Skipped ${CONFIG_TOML} (verify_jwt already false for mcp)`);
556
+ return;
557
+ }
558
+ await writeFile(CONFIG_TOML, toml);
559
+ console.log(`Updated ${CONFIG_TOML}: [functions.mcp] verify_jwt = false (broker tokens aren't Supabase JWTs)`);
560
+ }
441
561
  async function cmdInit() {
562
+ const brokerFlags = brokerFromFlags();
442
563
  await warnIfNotSupabaseProject();
443
564
  const { path: migrationPath, wasNew } = await ensureMetaMigration();
444
565
  console.log(wasNew ? `Wrote ${migrationPath}` : `Skipped ${migrationPath} (already scaffolded)`);
@@ -453,31 +574,41 @@ async function cmdInit() {
453
574
  if (connWarning) console.warn(`warn: ${connWarning}`);
454
575
  const rl = openInteractiveReadline();
455
576
  if (!rl) {
577
+ const broker = await resolveBroker(null, brokerFlags);
578
+ if (broker) await disableJwtVerification();
456
579
  if (!functionExists) {
457
- await writeFile(functionPath, buildFunctionTemplate());
580
+ await writeFile(functionPath, buildFunctionTemplate({ broker: broker ?? void 0 }));
458
581
  console.log(`Wrote ${functionPath}`);
459
582
  } else {
460
583
  console.log(`Skipped ${functionPath} (already exists)`);
584
+ if (broker) printBrokerMergeHint(functionPath, broker);
461
585
  }
462
- printManualCommands();
586
+ printManualCommands({ broker });
463
587
  return;
464
588
  }
465
589
  try {
466
590
  const conn = resolveConnectionInfo();
591
+ const broker = await resolveBroker(rl, brokerFlags);
592
+ if (broker) await disableJwtVerification();
467
593
  const anonymousSignIn = await askAnonymousSignIn(rl, conn);
468
594
  let introspection = conn ? await tryIntrospect(conn) : null;
469
595
  let alreadyPushed = false;
470
596
  let alreadyDeployed = false;
597
+ let brokerHintShown = false;
471
598
  if (!introspection) {
472
599
  if (!functionExists) {
473
- await writeFile(functionPath, buildFunctionTemplate({ anonymousSignIn }));
600
+ await writeFile(functionPath, buildFunctionTemplate({ anonymousSignIn, broker: broker ?? void 0 }));
474
601
  console.log(`Wrote ${functionPath}`);
475
602
  } else {
476
603
  console.log(`Skipped ${functionPath} (already exists)`);
604
+ if (broker) {
605
+ printBrokerMergeHint(functionPath, broker);
606
+ brokerHintShown = true;
607
+ }
477
608
  }
478
609
  if (!hasSupabaseCli()) {
479
610
  console.log("\nSupabase CLI not found on PATH -- install it to run the next steps automatically next time.");
480
- printManualCommands();
611
+ printManualCommands({ broker });
481
612
  return;
482
613
  }
483
614
  const connect = await promptYesNo(
@@ -485,24 +616,26 @@ async function cmdInit() {
485
616
  "\nApply the migration and deploy the function to whatever Supabase project is currently linked, now?"
486
617
  );
487
618
  if (!connect) {
488
- printManualCommands();
619
+ printManualCommands({ broker });
489
620
  return;
490
621
  }
491
- const result2 = pushAndDeploy();
622
+ const result2 = pushAndDeploy(broker);
492
623
  alreadyPushed = result2.pushed;
493
624
  alreadyDeployed = result2.deployed;
494
625
  if (!result2.deployed) {
495
626
  console.error(`
496
627
  Supabase CLI command failed: ${result2.error instanceof Error ? result2.error.message : result2.error}`);
497
- printManualCommands({ alreadyPushed });
628
+ printManualCommands({ alreadyPushed, broker });
498
629
  return;
499
630
  }
631
+ printLicenseKeyReminder(broker);
500
632
  introspection = conn ? await tryIntrospect(conn) : null;
501
633
  if (!introspection) {
502
634
  console.log(
503
635
  "\nDeployed, but couldn't reach the project to introspect tables (no SUPABASE_URL/SUPABASE_SERVICE_ROLE_KEY or --url/--key available). Re-run `exposify-supabase init` with those set to configure writes, RPCs, and documentation interactively."
504
636
  );
505
- console.log("\nDone. Paste the function URL + a user JWT into an MCP client to try it.");
637
+ console.log(`
638
+ Done. Paste the function URL + ${clientCredential(broker)} into an MCP client to try it.`);
506
639
  return;
507
640
  }
508
641
  }
@@ -513,14 +646,14 @@ Supabase CLI command failed: ${result2.error instanceof Error ? result2.error.me
513
646
  const writes = flattenWriteGrants(grants);
514
647
  let wroteFunctionFile = false;
515
648
  if (!functionExists) {
516
- await writeFile(functionPath, buildFunctionTemplate({ writes, rpc, anonymousSignIn }));
649
+ await writeFile(functionPath, buildFunctionTemplate({ writes, rpc, anonymousSignIn, broker: broker ?? void 0 }));
517
650
  console.log(`
518
651
  Wrote ${functionPath} with your selections.`);
519
652
  wroteFunctionFile = true;
520
- } else if (writes.length > 0 || rpc.length > 0) {
653
+ } else if (writes.length > 0 || rpc.length > 0 || broker && !brokerHintShown) {
521
654
  console.log(`
522
655
  ${functionPath} already exists -- left untouched. Merge this into your aiReady() config:`);
523
- console.log(buildFunctionTemplate({ writes, rpc, anonymousSignIn }));
656
+ console.log(buildFunctionTemplate({ writes, rpc, anonymousSignIn, broker: broker ?? void 0 }));
524
657
  }
525
658
  const commentsPath = await writeCommentsMigration(docs);
526
659
  if (commentsPath) console.log(`Wrote ${commentsPath}`);
@@ -531,26 +664,33 @@ ${functionPath} already exists -- left untouched. Merge this into your aiReady()
531
664
  }
532
665
  if (!hasSupabaseCli()) {
533
666
  console.log("\nSupabase CLI not found on PATH.");
534
- printManualCommands({ alreadyPushed: commentsPath === null, alreadyDeployed: !wroteFunctionFile });
667
+ printManualCommands({ alreadyPushed: commentsPath === null, alreadyDeployed: !wroteFunctionFile, broker });
535
668
  return;
536
669
  }
537
670
  const redeploy = await promptYesNo(rl, "\nApply/deploy these changes now?", true);
538
671
  if (!redeploy) {
539
- printManualCommands({ alreadyPushed: commentsPath === null, alreadyDeployed: !wroteFunctionFile });
672
+ printManualCommands({ alreadyPushed: commentsPath === null, alreadyDeployed: !wroteFunctionFile, broker });
540
673
  return;
541
674
  }
542
- const result = pushAndDeploy();
675
+ const result = pushAndDeploy(broker);
543
676
  if (!result.deployed) {
544
677
  console.error(`
545
678
  Supabase CLI command failed: ${result.error instanceof Error ? result.error.message : result.error}`);
546
- printManualCommands({ alreadyPushed: result.pushed });
679
+ printManualCommands({ alreadyPushed: result.pushed, broker });
547
680
  return;
548
681
  }
549
- console.log("\nDone. Paste the function URL + a user JWT into an MCP client to try it.");
682
+ printLicenseKeyReminder(broker);
683
+ console.log(`
684
+ Done. Paste the function URL + ${clientCredential(broker)} into an MCP client to try it.`);
550
685
  } finally {
551
686
  rl.close();
552
687
  }
553
688
  }
689
+ function printBrokerMergeHint(functionPath, broker) {
690
+ console.log(`
691
+ ${functionPath} already exists -- left untouched. Merge the broker wiring into your aiReady() config:`);
692
+ console.log(buildFunctionTemplate({ broker }));
693
+ }
554
694
  async function cmdGenerate() {
555
695
  const conn = resolveConnectionInfo();
556
696
  if (!conn) {
@@ -588,12 +728,17 @@ async function main() {
588
728
  const [, , cmd] = process.argv;
589
729
  if (cmd === "init") return cmdInit();
590
730
  if (cmd === "generate") return cmdGenerate();
591
- console.log("Usage: exposify-supabase <init|generate> [--url <url>] [--key <service-role-key>]");
731
+ console.log(
732
+ "Usage: exposify-supabase <init|generate> [--url <url>] [--key <service-role-key>] [--broker-url <url> --broker-project <id>]"
733
+ );
592
734
  console.log(" init Scaffold the exposify_meta() migration + supabase/functions/mcp/index.ts.");
593
735
  console.log(" Interactively offers to run `supabase db push` + `functions deploy`; if a");
594
736
  console.log(" project is reachable (SUPABASE_URL/SUPABASE_SERVICE_ROLE_KEY env vars, or --url/--key),");
595
737
  console.log(" also walks through anonymous sign-in, per-table writes, RPC exposure, and");
596
- console.log(" documentation for tables/columns missing a comment.");
738
+ console.log(" documentation for tables/columns missing a comment. Offers to wire in the hosted");
739
+ console.log(" broker (or pass --broker-url/--broker-project): adds createHostedAuthResolver to the");
740
+ console.log(" function, sets verify_jwt = false for it in supabase/config.toml, and deploys with");
741
+ console.log(" --no-verify-jwt.");
597
742
  console.log(" generate Build exposify.manifest.json from a running local/linked project");
598
743
  process.exitCode = cmd ? 1 : 0;
599
744
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exposify/supabase",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -27,7 +27,7 @@
27
27
  "prepack": "yarn build"
28
28
  },
29
29
  "dependencies": {
30
- "@exposify/core": "0.3.10",
30
+ "@exposify/core": "0.3.11",
31
31
  "@modelcontextprotocol/sdk": "^1.12.0",
32
32
  "zod": "^3.24.0"
33
33
  },
@@ -40,15 +40,6 @@
40
40
  "publishConfig": {
41
41
  "access": "public"
42
42
  },
43
- "repository": {
44
- "type": "git",
45
- "url": "git+https://github.com/ayadagain/agentready.git",
46
- "directory": "packages/supabase"
47
- },
48
- "homepage": "https://github.com/ayadagain/agentready/tree/main/packages/supabase#readme",
49
- "bugs": {
50
- "url": "https://github.com/ayadagain/agentready/issues"
51
- },
52
43
  "description": "Expose a Supabase-backed app's database and edge functions to AI agents over MCP, generated from PostgREST + Row Level Security — RLS is the authorization layer, so agents can do exactly what the logged-in user can do.",
53
44
  "author": "Exposify",
54
45
  "keywords": [