Agent Skills: Supabase Knowledge Patch

Supabase changes since training cutoff — OAuth 2.1 server, Web3 auth, Edge Function background tasks/S3 mounts, Realtime authorization, vector/analytics buckets, pgmq queues, MCP server, new API key model. Load before working with Supabase.

UncategorizedID: nevaberry/nevaberry-plugins/supabase-knowledge-patch

Install this agent skill to your local

pnpm dlx add-skill https://github.com/Nevaberry/nevaberry-plugins/tree/HEAD/plugins/supabase-knowledge-patch/skills/supabase-knowledge-patch

Skill Files

Browse the full folder contents for supabase-knowledge-patch.

Download Skill

Loading file tree…

plugins/supabase-knowledge-patch/skills/supabase-knowledge-patch/SKILL.md

Skill Metadata

Name
supabase-knowledge-patch
Description
"Supabase changes since training cutoff — OAuth 2.1 server, Web3 auth, Edge Function background tasks/S3 mounts, Realtime authorization, vector/analytics buckets, pgmq queues, MCP server, new API key model. Load before working with Supabase."

Supabase Knowledge Patch

Claude's baseline knowledge covers Supabase through mid-2025. This skill provides features and breaking changes from late 2025 onwards.

Index

Quick Reference: New API Key Model

| 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 },
})

Quick Reference: Auth

| 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
);

Quick Reference: Edge Functions

| 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 |

Quick Reference: Realtime

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 } } },
})

Quick Reference: Vector Buckets (Alpha)

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.

Quick Reference: Declarative Schemas

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"]

Quick Reference: Secret Management

# 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

Quick Reference: Queues (pgmq_public)

// 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>.

Quick Reference: JS Client (v2.74–v2.101)

| 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 |

Quick Reference: MCP Server

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.

Quick Reference: Sub-Minute Cron

SELECT cron.schedule('fast-poll', '30 seconds', 'SELECT process_pending()');

Valid: 1–59 seconds. Requires Postgres 15.1.1.61+.

Breaking Changes (2025–2026)

  • OpenAPI spec via anon key removed (Mar 2026) — use service_role or sb_secret_ key
  • pg_graphql disabled by default on new projects — CREATE EXTENSION pg_graphql to re-enable
  • PostgREST v14 JWT cache — enabled by default (~20% more RPS); disable: ALTER ROLE authenticator SET pgrst.jwt_cache_max_entries TO 0
  • Edge Functions recursive call rate limit (Mar 2026) — 5,000 req/min budget for function-to-function calls within a project