Patches Claude's knowledge of Supabase changes since mid-2025 including OAuth 2.1/Web3 auth, Edge Function background tasks/S3 mounts, Realtime RLS, vector/analytics buckets, pgmq queues, new API keys, MCP server. Use before Supabase projects.
npx claudepluginhub nevaberry/nevaberry-plugins --plugin supabase-knowledge-patchThis skill uses the workspace's default tool permissions.
Implements structured self-debugging workflow for AI agent failures: capture errors, diagnose patterns like loops or context overflow, apply contained recoveries, and generate introspection reports.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Claude's baseline knowledge covers Supabase through mid-2025. This skill provides features and breaking changes from late 2025 onwards.
waitUntil), S3 mounts, ephemeral storage, getClaims() auth pattern, limitsoverrideTypes<>(), JSON field inference, Python type genskipAutoInitialize, CORS headers exportpgmq_public queues via PostgRESTenv()/encrypted:), deployment DAG, CLI commands| Type | Format | Replaces | Notes |
|---|---|---|---|
| Publishable | sb_publishable_... | anon JWT | Safe for client-side |
| Secret | sb_secret_... | service_role JWT | Browser-blocked (401) |
Both old and new keys work simultaneously. Not JWTs — gateway mints short-lived JWT internally. Platform-only (not CLI/self-hosted).
// Client-side
const supabase = createClient(SUPABASE_URL, 'sb_publishable_...')
// Server-side
const supabase = createClient(SUPABASE_URL, 'sb_secret_...', {
auth: { persistSession: false },
})
| Feature | API |
|---|---|
| OAuth 2.1 server | supabase.auth.oauth.approveAuthorization(id) |
| Custom OAuth/OIDC | supabase.auth.admin.customProviders.createProvider({...}) |
| Web3 (Ethereum/Solana) | supabase.auth.signInWithWeb3({ chain: 'ethereum' }) |
| JWT claims in Edge Fn | supabase.auth.getClaims(token) |
| Throw on error | createClient(url, key, { auth: { throwOnError: true } }) |
| Skip auto-init (SSR) | createClient(url, key, { auth: { skipAutoInitialize: true } }) |
| Before user hook | SQL function returning { "error": {...} } to reject signup |
OAuth tokens include client_id claim — use in RLS to restrict OAuth client access:
CREATE POLICY "No OAuth access" ON sensitive_table FOR ALL USING (
auth.uid () = user_id
AND (auth.jwt () ->> 'client_id') IS NULL
);
| Feature | Usage |
|---|---|
| Background tasks | EdgeRuntime.waitUntil(promise) |
| S3 mount read/write | Deno.readFile('/s3/BUCKET/path') |
Ephemeral /tmp | 256MB free, 512MB paid; sync APIs only at top level |
| Wall clock | 150s free / 400s paid |
| CPU per request | 2s |
| Bundle size | 20MB |
| Functions/project | 100 free / 500 pro / 1000 team |
Private channels require RLS on realtime.messages + private: true:
await supabase.realtime.setAuth()
const channel = supabase.channel('room:123', { config: { private: true } })
Server-side broadcast: SELECT realtime.send(payload, event, topic, private).
Broadcast replay (alpha, private channels only):
const channel = supabase.channel('room:main', {
config: { private: true, broadcast: { replay: { since: timestamp, limit: 10 } } },
})
const bucket = supabase.storage.vectors.from('embeddings');
await bucket.createIndex({
indexName: 'docs',
dataType: 'float32',
dimension: 1536,
distanceMetric: 'cosine',
});
const index = bucket.index('docs');
await index.putVectors({
vectors: [
{ key: 'doc-1', data: { float32: embedding }, metadata: { title: 'Doc' } },
],
});
const { data } = await index.queryVectors({
queryVector: { float32: query },
topK: 10,
filter: { category: 'guide' },
});
SQL access via FDW: WHERE data <==> '[...]'::embd ORDER BY embd_distance(data). Limits: 10 buckets, 10 indexes/bucket, 4096 dims.
Place desired-state SQL in supabase/schemas/, then diff to generate migrations:
supabase db diff -f create_employees_table
[db.migrations]
schema_paths = ["./schemas/employees.sql", "./schemas/*.sql"]
# env() — reference env vars in any config field
[auth.external.github]
client_id = "env(GITHUB_CLIENT_ID)"
secret = "env(GITHUB_SECRET)"
supabase secrets set --env-file ./supabase/.env
// Send
await supabase.schema('pgmq_public').rpc('send', {
queue_name: 'my_queue',
message: { hello: 'world' },
sleep_seconds: 0,
});
// Read (with visibility timeout)
await supabase.schema('pgmq_public').rpc('read', {
queue_name: 'my_queue',
sleep_seconds: 30,
n: 5,
});
// Pop (read + delete)
await supabase.schema('pgmq_public').rpc('pop', { queue_name: 'my_queue' });
Requires: enable pgmq extension, toggle "Expose Queues via PostgREST", enable RLS on pgmq.q_<name>.
| Change | Version | Details |
|---|---|---|
| Node.js 20+ required | v2.79.0+ | @supabase/node-fetch removed; pin v2.78.0 for Node 18 |
notin() filter | — | .notin('id', [1,2,3]) |
isdistinct() filter | — | .isdistinct('deleted_at', null) — NULL-safe |
overrideTypes<>() | v2.48.0+ | Override query result types |
| Auth throw mode | v2.79.0+ | { auth: { throwOnError: true } } |
detectSessionInUrl fn | v2.88.0+ | Accepts predicate function |
| Functions timeout | v2.81.0+ | signal: AbortSignal.timeout(30000) |
| CORS headers | v2.95.0+ | import { corsHeaders } from '@supabase/supabase-js/cors' |
DatabaseWithoutInternals | v2.89.0+ | Strips internal schemas from Database type |
Remote: https://mcp.supabase.com/mcp (HTTP transport, OAuth 2.1 auth).
Local CLI: http://localhost:54321/mcp (no OAuth).
Config via URL params: ?project_ref=...&read_only=true&features=database,docs
Feature groups: account, docs, database, debugging, development, functions, storage, branching. All enabled by default except storage.
SELECT cron.schedule('fast-poll', '30 seconds', 'SELECT process_pending()');
Valid: 1–59 seconds. Requires Postgres 15.1.1.61+.
service_role or sb_secret_ keyCREATE EXTENSION pg_graphql to re-enableALTER ROLE authenticator SET pgrst.jwt_cache_max_entries TO 0