Skip to main content
Glama

RLS check for Supabase: who can read your customers' data?

A read-only query you run in your own Supabase SQL Editor, and an analyser that explains the result table by table:

  • which tables anyone with the public (anon) key can read or change;

  • which tables let any signed-in user read other users' rows;

  • which tables are locked, and which are safe.

The findings are written in Arabic for non-technical founders, with English labels (exposed, check it, locked, safe), and each one links to an Arabic lesson on the fix.

  • It reads no data. The query reads table descriptions only: RLS state, policies, grants and column names. Never a row.

  • It needs no key or password and opens no connection to your database. You run the query yourself, or an agent runs it through the official Supabase MCP.

  • It is the same check as the browser tool at muhandis.online/tools/rls-check: lib/rls-check.js is the site's analyser, copied byte for byte.

Usage

1. In the browser (easiest)

Open the check page, copy the query, run it in Supabase and paste the result. Everything happens in your browser; nothing is sent anywhere.

2. From the command line

npx muhandis-rls-check query > query.sql     # the query: run it in your SQL Editor
npx muhandis-rls-check result.json           # paste the result cell into a file; this explains it
npx muhandis-rls-check result.json --json    # the same explanation as JSON

It exits with 1 when a table is exposed, so it can be a CI step.

3. For an AI assistant (MCP)

Add it to Claude, Cursor or any MCP client. The assistant runs the query (through the official Supabase MCP, for example) and this server explains the result. The server itself holds no connection details.

{
  "mcpServers": {
    "muhandis-rls-check": { "command": "npx", "args": ["-y", "muhandis-rls-check", "mcp"] }
  }
}

Tools: rls_check_query returns the query, rls_check_explain explains a result.

4. As a Claude skill

The same check as a Claude skill: Claude gives you the query, then runs the pasted result through skills/rls-check/scripts/rls_check.py, a Python copy of the analyser that the tests hold to the same output as lib/rls-check.js. It needs no key and makes no network call.

  • Claude app: download rls-check.zip from the latest release, then Customize > Skills, upload it and turn it on. Code execution must be enabled.

  • Claude Code:

/plugin marketplace add Muhandis-online/rls-check
/plugin install rls-check@muhandis

Related MCP server: schema-guard-mcp

What it checks

Label

Arabic

Meaning

exposed

مكشوف

RLS is off and the visitor role has a grant; or a policy whose condition is true on a table with personal columns; or a view without security_invoker that visitors can read; or an update or delete policy open to everyone

check it

تأكد منه

Data everyone can read that has no personal columns, such as a product list; or inserts without sign-in, such as a contact form

locked

مقفول

RLS on with no policies, or no grants: the app cannot reach it

safe

سليم

Policies tied to the row's owner through auth.uid()

Personal columns are recognised by name (email, phone, user_id, iban, national, iqama and similar).

Limits

This checks the rules you can see; it does not replace a test. After it, sign in with two accounts and confirm each one sees only its own rows. Supabase's own Security Advisor flags related issues (RLS disabled, security-definer views, permissive policies); this tool adds an explanation in Arabic for the product's owner, and a link to how to fix each finding.

Found an exposed table and want an engineer to fix it with you? muhandis.online/services/fix


Made by Muhandis, a software company for businesses in Saudi Arabia and the GCC. MIT licence.

Available Tools

2 tools
rls_check_explainRLS check: explain the resultA
Read-only

Explains the output of rls_check_query table by table, worst first: which tables anyone with the public (anon) key can read or change, which any signed-in user can read across users, which are locked, which are safe. Findings are in Arabic for founders (labels also given in English) and link to Arabic lessons on muhandis.online. Works offline on the pasted result.

NameRequiredDescriptionDefault
resultYesThe JSON result cell of rls_check_query, as text (it starts with {"tool":"muhandis-rls-check"...)

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already establish readOnlyHint=true and openWorldHint=false, so the safety profile is covered. The description adds useful behavioral detail: findings are ordered worst first, grouped by risk class, written in Arabic with English labels, and linked to Arabic lessons.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but front-loaded, first stating what is explained and then adding ordering, language, linking, and offline behavior. Every clause carries relevant information, though it could be slightly tighter.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter, read-only explanation tool with no output schema, the description gives enough detail about what the findings contain, how they are ordered, what languages are used, and that it works offline. Nothing essential is missing for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, and the single result parameter is already documented as the JSON result cell from rls_check_query. The description reinforces that it operates on a pasted result, but adds no new syntax or format details beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb/resource: it explains the output of rls_check_query table by table. It also distinguishes the tool from the sibling query tool by emphasizing explanation of a pasted result, not execution of a check.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It clearly indicates the tool is used on the result from rls_check_query and that it works offline on the pasted result. It does not explicitly state exclusions or when not to use it, but the context is strong and unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

rls_check_queryRLS check: the read-only queryA
Read-only

Returns a read-only SQL query that lists every table and view in the public schema of a Supabase (Postgres) project with its RLS state, grants to anon/authenticated, column names and policies. It reads no row of data. Run it in the project (SQL Editor, or a Supabase MCP execute_sql tool), then pass the single JSON result cell to rls_check_explain.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true and openWorldHint=false, and the description adds non-obvious behavior the annotations cannot express: this tool emits a query rather than executing it, touches zero rows, and returns a single JSON result cell that must be handed off. That is exactly the extra context the bar asks for when annotations exist.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, each load-bearing: what it returns, what it does not touch, and how to run and hand off the result. The scope detail is front-loaded before the operational instructions.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description still characterizes the return value (a SQL query text, whose execution yields a single JSON cell) and the downstream consumer. Nothing an agent needs to call this correctly — where to run it, what comes back, where it goes next — is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema is an empty object with zero parameters, so there is nothing for the description to disambiguate and no risk of a caller mis-passing arguments. Baseline 4 applies; no parameter meaning is added or needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and artifact ('Returns a read-only SQL query') plus the exact payload it enumerates: every table and view in the public schema with RLS state, anon/authenticated grants, columns, and policies. It is unambiguously distinct from its only sibling, which consumes the output rather than producing it.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete execution contexts ('SQL Editor, or a Supabase MCP execute_sql tool') and the explicit next step ('pass the single JSON result cell to rls_check_explain'), which routes the agent through the sibling pair. It also pre-empts a likely misreading by stating it reads no row of data.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.1.1
    • First observedrls_check_explain
    • First observedrls_check_query

TDQS

A4.5/5.0

Scored across 2 tools

Disambiguation5/5

The two tools have clearly distinct roles: one generates a read-only SQL query, the other explains the resulting JSON output. The descriptions explicitly describe a sequential handoff, leaving no realistic chance of misselection.

Naming Consistency5/5

Both names follow the same rls_check_<action> snake_case pattern, with query and explain as clear action suffixes. The convention is fully consistent across the set.

Tool Count4/5

Two tools is on the thin side generally, but for this narrowly scoped two-step RLS audit workflow each tool earns its place. There is no obvious extra operation that would justify adding more tools.

Completeness5/5

The pair covers the full lifecycle: generate the diagnostic query, run it externally, then explain the pasted result. The query tool states it reads no row data and the explain tool works offline, so there are no apparent dead ends for the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    This is an MCP server for PostgREST. It allows LLMs perform database queries and operations on Postgres databases via PostgREST. This server works with both Supabase projects (which use PostgREST) and standalone PostgREST servers.
    2,041 npm
    2,990
    Apache 2.0
  • A
    license
    A
    quality
    D
    maintenance
    Audits PostgreSQL/Supabase schemas for security issues like missing RLS, permissive policies, and sensitive data exposure during AI conversations.
    3
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables safe, read-only SQL exploration and debugging on Supabase Edge Functions, respecting user JWTs. It inspects schemas, explains query plans, proposes RLS policies, and runs guarded queries without destructive operations.
    MIT