@rip-user/rls-debugger-mcp
Version:
AI-powered MCP server for debugging Supabase Row Level Security policies with Claude structured outputs
327 lines (279 loc) • 8.81 kB
Markdown
# Usage Examples
This document provides real-world examples of using the RLS Policy Debugger MCP server.
## Example 1: Debugging Access Denial
### Scenario
A user with role 'admin' in an organization can't see their documents, even though they should have access.
### Query to Claude
```
Using the rls-debugger MCP server, analyze why a team admin
can't see documents. The user is authenticated and has the admin
role in the team_members table.
```
### What Happens
1. Tool fetches all policies for `documents`
2. Loads any saved knowledge about this table
3. Claude analyzes each policy:
- Checks PERMISSIVE policies (OR logic)
- Checks RESTRICTIVE policies (AND logic)
- Evaluates relationships via EXISTS clauses
4. Identifies root cause
5. Provides verification SQL
6. Suggests fixes with risk levels
### Expected Output
```json
{
"scenario_summary": "Admin user cannot access their documents despite having admin role",
"applicable_policies": [
{
"table": "documents",
"policy_name": "team_members_can_view",
"operation": "SELECT",
"condition": "EXISTS (SELECT 1 FROM team_members WHERE ...)",
"will_grant_access": false,
"reason": "EXISTS check fails - no matching team_members record found",
"policy_type": "PERMISSIVE"
}
],
"policy_combination_logic": "At least one PERMISSIVE policy must pass. Currently all PERMISSIVE policies are failing.",
"root_cause": {
"issue": "Missing team_members record for the admin",
"missing_condition": "team_members entry linking user to organization"
},
"required_relationships": [
{
"table": "team_members",
"condition": "user_id = 'abc-123' AND org_id = 'xyz-789'",
"exists": false,
"verification_query": "SELECT * FROM team_members WHERE user_id = 'abc-123' AND org_id = 'xyz-789'"
}
],
"recommendations": [
{
"action": "Create team_members record",
"sql": "INSERT INTO team_members (user_id, org_id, role) VALUES ('abc-123', 'xyz-789', 'admin')",
"explanation": "Add the missing relationship that the policy requires",
"risk_level": "low"
}
]
}
```
## Example 2: Understanding Complex Policy Logic
### Query
```
Show me how all policies on the documents table combine
```
### Use Tool
```
compile_policy_logic with table_name = "documents"
```
### Output
```json
{
"table": "documents",
"rls_enabled": true,
"access_logic": "At least ONE PERMISSIVE policy must pass (OR logic)",
"additional_restrictions": "No additional restrictions",
"permissive_policies": [
{
"name": "team_members_can_view",
"operation": "SELECT",
"condition": "EXISTS (SELECT 1 FROM team_members tm WHERE tm.org_id = documents.org_id AND tm.user_id = auth.uid())",
"parsed": {
"requires_auth": true,
"checks_relationships": true,
"referenced_tables": ["team_members"],
"joins": ["tm.org_id = documents.org_id", "tm.user_id = auth.uid()"],
"comparisons": ["tm.org_id = documents.org_id", "tm.user_id = auth.uid()"]
}
}
],
"restrictive_policies": [],
"decision_flow": "PERMISSIVE POLICIES (need at least ONE to pass):\n 1. Check \"team_members_can_view\" (SELECT): EXISTS (...)\n\nFINAL DECISION: GRANT if any PERMISSIVE policy passes"
}
```
## Example 3: Saving Knowledge for Future Reference
### After Fixing an Issue
```
After fixing the documents access issue, save this to memory:
The documents table requires a team_members record for access.
New admins were missing these records. Fixed by updating the admin
creation process to always create a team_members entry.
```
### Tool Call
```json
{
"tool": "save_policy_knowledge",
"arguments": {
"knowledge_type": "correction",
"content": "documents requires team_members record. Fixed admin creation process to always create team_members entry.",
"related_tables": ["documents", "team_members"],
"related_policies": ["team_members_can_view"]
}
}
```
### Next Time
When analyzing `documents`, Claude will automatically see this saved knowledge and can reference it in the analysis.
## Example 4: Investigating Policy Conflicts
### Scenario
You have multiple policies and suspect they might be conflicting.
### Query
```
I have three policies on my posts table:
1. users_own_posts - users can see their own posts
2. public_posts - anyone can see public posts
3. active_posts - only active posts are visible
Analyze if these policies work together correctly.
```
### Analysis
The tool will:
1. Fetch all policies
2. Identify that policies 1 and 2 are PERMISSIVE (OR logic)
3. Identify if policy 3 is PERMISSIVE or RESTRICTIVE
4. Explain how they combine
5. Point out if there are issues
### If Policy 3 is PERMISSIVE (Wrong)
```json
{
"root_cause": {
"issue": "active_posts policy is PERMISSIVE but should be RESTRICTIVE",
"incorrect_policy": "active_posts"
},
"recommendations": [
{
"action": "Change active_posts to RESTRICTIVE",
"sql": "DROP POLICY active_posts ON posts;\nCREATE POLICY active_posts ON posts FOR SELECT AS RESTRICTIVE USING (status = 'active');",
"explanation": "Make it RESTRICTIVE so it applies to all rows, not as an OR condition",
"risk_level": "low"
}
]
}
```
## Example 5: Checking All Policies in Database
### Query
```
List all RLS policies in my database
```
### Tool Call
```json
{
"tool": "list_all_policies"
}
```
### Output
```json
[
{
"schemaname": "public",
"tablename": "documents",
"policyname": "team_members_can_view",
"polpermissive": "PERMISSIVE",
"policyrole": "authenticated",
"polcmd": "SELECT",
"policyqual": "EXISTS (SELECT 1 FROM team_members ...)",
"policywithcheck": null
},
{
"schemaname": "public",
"tablename": "posts",
"policyname": "users_own_posts",
"polpermissive": "PERMISSIVE",
"policyrole": "authenticated",
"polcmd": "ALL",
"policyqual": "(user_id = auth.uid())",
"policywithcheck": "(user_id = auth.uid())"
}
]
```
## Example 6: Verifying a Fix
### Before Fix
```
Analyze documents access for user abc-123
```
Result: Access denied, missing team_members record
### Apply Fix
```sql
INSERT INTO team_members (user_id, org_id, role)
VALUES ('abc-123', 'xyz-789', 'admin');
```
### After Fix
```
Re-analyze documents access for user abc-123
```
Result: Access granted, policy passes
### Save the Fix
```
Save to memory: Fixed documents access by adding missing team_members record
```
## Example 7: Multi-Table Analysis
### Scenario
Your app has complex relationships across multiple tables.
### Query
```
Analyze why user abc-123 can't update their profile, considering the
users, profiles, and subscriptions tables
```
### What Happens
1. Fetches policies for all three tables
2. Loads saved knowledge about all three
3. Analyzes how policies interact across tables
4. Checks for missing relationships
5. Identifies which table's policy is blocking access
## Tips for Effective Usage
### 1. Be Specific in Scenarios
❌ "User can't access data"
✅ "User abc-123 with admin role can't see document xyz-456 even though they're a team admin"
### 2. Save Knowledge After Fixes
Always save what you learned:
```
Save to memory: [description of issue and fix]
```
This builds a knowledge base that helps with future debugging.
### 3. Start with Policy Logic
Before diving into analysis, understand the structure:
```
Compile policy logic for [table_name]
```
### 4. Verify Relationships
If policies check EXISTS clauses, verify the data exists:
```
Run the verification query from the analysis results
```
### 5. Use Memory Summary
See what you've learned over time:
```
Get memory summary
```
## Common Patterns
### Pattern 1: Ownership + Admin Access
```sql
-- Users can see their own records
CREATE POLICY "own_records" ON table FOR SELECT
USING (user_id = auth.uid());
-- Admins can see all records
CREATE POLICY "admin_access" ON table FOR SELECT
USING (is_admin(auth.uid()));
```
Analysis: OR logic - passes if EITHER condition is true
### Pattern 2: Base Access + Restrictions
```sql
-- Base access (PERMISSIVE)
CREATE POLICY "base_access" ON table FOR SELECT
USING (is_member(auth.uid()));
-- Must be active (RESTRICTIVE)
CREATE POLICY "must_be_active" ON table FOR SELECT AS RESTRICTIVE
USING (status = 'active');
```
Analysis: Must pass base access AND restriction
### Pattern 3: Role-Based with Relationships
```sql
CREATE POLICY "role_access" ON posts FOR SELECT
USING (
EXISTS (
SELECT 1 FROM team_members
WHERE team_members.user_id = auth.uid()
AND team_members.team_id = posts.team_id
AND team_members.role IN ('admin', 'editor')
)
);
```
Analysis: Checks authentication, relationship, and role