npm package: @rip-user/rls-debugger-mcp
Description: AI-powered MCP server for debugging Supabase Row Level Security policies with Claude structured outputs
Author: rip-user
Latest version: 0.1.0-beta.1

# RLS Policy Debugger MCP Server

An AI-powered Model Context Protocol (MCP) server for debugging Supabase Row Level Security (RLS) policies. This tool uses Claude's advanced reasoning to analyze complex policy interactions, identify access issues, and suggest fixes with guaranteed structured outputs.

## Features

- **AI-Powered Analysis**: Uses Claude Sonnet 4.5 with structured outputs to analyze RLS policies
- **Policy Logic Compilation**: Generates human-readable decision trees showing how policies combine
- **Memory System**: Remembers architectural decisions and known issues across debugging sessions
- **Root Cause Identification**: Pinpoints exactly why access is granted or denied
- **SQL Recommendations**: Provides ready-to-use SQL statements to fix policy issues
- **Risk Assessment**: Evaluates the safety of recommended changes

## Why This Tool?

The official Supabase MCP server can create and manage RLS policies, but it:
- Throws errors on policies with spaces in names
- Lacks understanding of how policies interact
- Can't analyze the "big picture" of your security model
- Doesn't provide debugging insights

This RLS Debugger MCP server is purpose-built for debugging complex RLS scenarios where multiple policies interact in unexpected ways.

## Installation

### Via Claude Code CLI (Recommended)

For Claude Code users, install directly using the MCP CLI:

```bash
# Project-specific installation
claude mcp add --transport stdio \
  --command "npx" \
  --arg "-y" \
  --arg "@rip-user/rls-debugger-mcp" \
  rls-debugger

# Global installation (all projects)
claude mcp add --scope user --transport stdio \
  --command "npx" \
  --arg "-y" \
  --arg "@rip-user/rls-debugger-mcp" \
  rls-debugger
```

**Windows users**: Use `cmd /c npx` instead of `npx`:
```bash
claude mcp add --transport stdio \
  --command "cmd" \
  --arg "/c" \
  --arg "npx" \
  --arg "-y" \
  --arg "@rip-user/rls-debugger-mcp" \
  rls-debugger
```

The CLI will add environment variables interactively. You'll be prompted for:
- `SUPABASE_URL`: Your Supabase project URL
- `SUPABASE_SERVICE_KEY`: Service role key
- `ANTHROPIC_API_KEY`: Your Anthropic API key

### Manual Installation

```bash
npm install -g @rip-user/rls-debugger-mcp
# or
npx @rip-user/rls-debugger-mcp
```

### From Source

```bash
git clone https://github.com/rip-user/rls-debugger-mcp
cd rls-debugger-mcp
npm install
npm run build
```

## Configuration

### For Claude Desktop

Add to your `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "rls-debugger": {
      "command": "node",
      "args": ["/path/to/Supabase-rls-mcp/dist/index.js"],
      "env": {
        "SUPABASE_URL": "https://your-project.supabase.co",
        "SUPABASE_SERVICE_KEY": "your-service-role-key",
        "ANTHROPIC_API_KEY": "your-anthropic-api-key",
        "MEMORY_DIR": "./.rls-memory"
      }
    }
  }
}
```

### For Claude Code

Three configuration scopes available:

**1. Project-specific** (`.mcp.json`):
```json
{
  "mcpServers": {
    "rls-debugger": {
      "command": "npx",
      "args": ["-y", "@rip-user/rls-debugger-mcp"],
      "env": {
        "SUPABASE_URL": "https://your-project.supabase.co",
        "SUPABASE_SERVICE_KEY": "your-service-role-key",
        "ANTHROPIC_API_KEY": "your-anthropic-api-key"
      }
    }
  }
}
```

**2. User-global** (`~/.mcp.json`):
Same as above, but applies to all projects.

**3. Local override** (`.mcp.local.json`):
Project-specific overrides (gitignored). Use for development or different credentials per project.

## Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `SUPABASE_URL` | Yes | Your Supabase project URL |
| `SUPABASE_SERVICE_KEY` | Yes | Service role key (has admin access) |
| `ANTHROPIC_API_KEY` | Yes | Anthropic API key for Claude |
| `CLAUDE_MODEL` | No | Claude model to use (default: `claude-sonnet-4-5-20250514`) |
| `MEMORY_DIR` | No | Directory for storing policy knowledge (default: `./.rls-memory`) |

**Model Configuration**:
- Default: `claude-sonnet-4-5-20250514` (fast, cost-effective)
- For thorough analysis: `claude-opus-4-1-20250514` (slower, more detailed)
- Set via `CLAUDE_MODEL` environment variable

## Available Tools

### `analyze_rls_policies`

Analyzes RLS policies using AI to debug access issues.

**Parameters:**
- `scenario` (required): Describe the access problem (e.g., "Why can't user X see their order?")
- `table_name` (optional): Focus on a specific table

**Returns:** Structured analysis including:
- Scenario summary
- Applicable policies and whether they grant access
- Policy combination logic explanation
- Root cause of the issue
- Required relationships verification
- Recommendations with SQL and risk levels

**Example:**
```
Using the rls-debugger MCP server, analyze why a team admin
with role 'admin' can't see their documents in the documents table
```

### `compile_policy_logic`

Compiles all RLS policies for a table into a decision tree.

**Parameters:**
- `table_name` (required): Table to analyze

**Returns:** Logic tree showing:
- PERMISSIVE policies (OR logic)
- RESTRICTIVE policies (AND logic)
- Complete decision flow
- Parsed conditions (auth checks, relationships, joins)

### `list_all_policies`

Lists all RLS policies in your database.

**Parameters:**
- `table_name` (optional): Filter by table

**Returns:** Array of policies with details:
- Policy name, type (PERMISSIVE/RESTRICTIVE)
- Operation (SELECT, INSERT, UPDATE, DELETE, ALL)
- USING and WITH CHECK clauses
- Role the policy applies to

### `save_policy_knowledge`

Saves insights to memory for future debugging sessions.

**Parameters:**
- `knowledge_type`: `policy_intent`, `known_issue`, `architectural_decision`, or `correction`
- `content`: The knowledge to save
- `related_tables`: Array of table names
- `related_policies` (optional): Array of policy names

**Example:**
```
Save to memory: The documents table uses a two-tier access model where
team admins need both a team_members record AND admin role
```

### `get_policy_knowledge`

Retrieves saved knowledge from memory.

**Parameters:**
- `table_name` (optional): Filter by table
- `knowledge_type` (optional): Filter by type

### `get_memory_summary`

Gets statistics about saved knowledge.

**Returns:**
- Total entries
- Breakdown by type
- Breakdown by table

## How It Works

### 1. Structured Outputs (Nov 14, 2025 API)

Uses Claude's native `response_format` with JSON Schema for guaranteed structured responses:

```typescript
const response = await anthropic.messages.create({
  model: 'claude-sonnet-4-5-20250514',
  betas: ["structured-outputs-2025-11-13"],
  response_format: {
    type: "json_schema",
    json_schema: {
      name: "rls_analysis",
      strict: true,
      schema: {
        type: 'object',
        properties: {
          scenario_summary: { type: 'string' },
          applicable_policies: { type: 'array', items: {...} },
          root_cause: { type: 'object', ... },
          recommendations: { type: 'array', ... }
        },
        required: [...],
        additionalProperties: false
      }
    }
  },
  messages: [...]
});
```

This ensures every analysis follows the same structure with zero parsing errors.

### 2. Policy Fetching from PostgreSQL

Queries PostgreSQL system tables (`pg_policy`, `pg_class`, `pg_namespace`) to get complete policy information:

```sql
SELECT
  nsp.nspname AS schemaname,
  rel.relname AS tablename,
  pol.polname AS policyname,
  CASE pol.polpermissive
    WHEN true THEN 'PERMISSIVE'
    WHEN false THEN 'RESTRICTIVE'
  END AS polpermissive,
  CASE pol.polcmd
    WHEN 'r' THEN 'SELECT'
    WHEN 'a' THEN 'INSERT'
    ...
  END AS polcmd,
  pg_get_expr(pol.polqual, pol.polrelid) AS policyqual,
  pg_get_expr(pol.polwithcheck, pol.polrelid) AS policywithcheck
FROM pg_catalog.pg_policy pol
...
```

### 3. Memory System

Stores debugging insights in a JSON file for persistence across sessions:

```json
[
  {
    "type": "architectural_decision",
    "content": "documents uses dual-key access: team_members + role check",
    "tables": ["documents", "team_members"],
    "timestamp": "2025-01-15T10:30:00Z"
  }
]
```

### 4. Policy Logic Compilation

Parses policy conditions to extract:
- Authentication requirements (`auth.uid()`)
- Relationship checks (`EXISTS` clauses)
- Referenced tables
- Join conditions
- Comparison operators

## Understanding RLS Policy Logic

### PERMISSIVE Policies (Default)

Use **OR logic** - if ANY policy grants access, the user can access the row:

```sql
-- Policy 1: User owns the record
CREATE POLICY "users_own_records" ON posts
  FOR SELECT USING (user_id = auth.uid());

-- Policy 2: Record is public
CREATE POLICY "public_posts" ON posts
  FOR SELECT USING (is_public = true);

-- Result: User can see a post if EITHER:
--   - They own it, OR
--   - It's public
```

### RESTRICTIVE Policies

Use **AND logic** - ALL policies must pass:

```sql
CREATE POLICY "must_be_active" ON posts
  FOR SELECT AS RESTRICTIVE
  USING (status = 'active');

-- Result: User can see a post if:
--   (owns it OR it's public) AND status = 'active'
```

### Common Issues This Tool Detects

1. **Missing Relationships**: Policy checks for a record that doesn't exist
2. **Policy Conflicts**: Multiple PERMISSIVE policies that never both pass
3. **Incorrect Policy Type**: Should be RESTRICTIVE but defined as PERMISSIVE
4. **Missing WITH CHECK**: INSERT/UPDATE allowed but shouldn't be
5. **Overly Restrictive**: Multiple conditions that are impossible to satisfy together

## Example Usage

### Scenario 1: Debugging Access Denial

```
I have a user with UUID 'abc-123' who is an admin in organization 'xyz-789',
but they can't see their documents. Why?
```

The tool will:
1. Fetch all policies for `documents`
2. Load any saved knowledge about this table
3. Analyze each policy to see if it would grant access
4. Identify the root cause (e.g., missing `team_members` record)
5. Provide verification SQL to check relationships
6. Suggest fixes with risk assessment

### Scenario 2: Understanding Policy Logic

```
Show me how all the policies on the user_roles table combine
```

Returns a decision tree:
```
PERMISSIVE POLICIES (need at least ONE to pass):
  1. Check "user_roles_select_policy":
     EXISTS (SELECT 1 FROM organizations WHERE id = org_id AND owner_id = auth.uid())
  OR
  2. Check "admin_access":
     role = 'admin' AND user_id = auth.uid()

FINAL DECISION: GRANT if any PERMISSIVE policy passes
```

### Scenario 3: Saving Knowledge

After fixing an issue:

```
Save to memory: Fixed documents access by ensuring team_members
records exist for all admins. The issue was new admins weren't getting
team_members entries created.
```

Next time you analyze `documents`, this knowledge will be included in the analysis.

## Companion Skills

Optional Claude Skills enhance the MCP server with systematic debugging workflows and RLS best practices.

**Installation** (copy to your Skills directory):
```bash
# Global installation (recommended)
mkdir -p ~/.claude/skills
cp -r examples/skills/rls-debugger ~/.claude/skills/

# Project-local installation
mkdir -p .claude/skills
cp -r examples/skills/rls-debugger .claude/skills/
```

**What Skills provide**:
- 5-step systematic debugging workflow
- Common RLS pattern recognition (multi-tenant, role-based, hierarchical)
- Performance optimization tips
- Issue detection and best practices
- Automatic activation when you mention RLS debugging

See `examples/skills/rls-debugger/README.md` for details.

## Development

```bash
# Install dependencies
npm install

# Build TypeScript
npm run build

# Watch mode during development
npm run watch

# Run directly with tsx (development)
npm run dev
```

## How This Differs from Official Supabase MCP

| Feature | Official Supabase MCP | RLS Debugger MCP |
|---------|----------------------|------------------|
| Create/modify policies | ✅ | ❌ |
| List policies | ✅ | ✅ |
| Analyze policy interactions | ❌ | ✅ |
| Debug access denials | ❌ | ✅ |
| Explain policy logic | ❌ | ✅ |
| Remember fixes | ❌ | ✅ |
| Structured analysis output | ❌ | ✅ |
| Suggest fixes with SQL | ❌ | ✅ |
| Risk assessment | ❌ | ✅ |

**Use together:** Official Supabase MCP for policy management + RLS Debugger MCP for debugging.

## Requirements

- Node.js 18 or later
- Supabase project with service role key
- Anthropic API key
- TypeScript 5.7+ (for development)

## Roadmap

### v0.2.0 (Planned)
- **Async operations**: Long-running analysis with progress updates
- **MCP Nov 25, 2025 spec**: Support for asynchronous tool execution
- **Batch analysis**: Analyze multiple tables/policies concurrently
- **Enhanced memory**: Vector-based semantic search for saved knowledge

### v0.1.0-beta (Current)
- ✅ Structured outputs (Nov 14, 2025 API)
- ✅ MCP 2025-06-18 compliance (outputSchema, title fields)
- ✅ Configurable model selection
- ✅ Companion Skills
- ✅ Generic examples (non-domain-specific)

## License

MIT

## Contributing

Issues and pull requests welcome!

## Credits

Built using:
- [Model Context Protocol SDK](https://github.com/modelcontextprotocol/typescript-sdk) (v1.22.0)
- [Anthropic SDK](https://github.com/anthropics/anthropic-sdk-typescript) for Claude AI
- [Supabase JS Client](https://github.com/supabase/supabase-js)
- Claude Sonnet 4.5 for AI-powered analysis