Supabase MCP Server
Supabase MCP Server
Section titled “Supabase MCP Server”The Supabase MCP server provides comprehensive database operations, project management, and real-time functionality for your Supabase projects directly through Claude.
Overview
Section titled “Overview”This server enables full Supabase project management including:
- Database schema creation and management
- SQL query execution and migrations
- User authentication and management
- Project configuration and monitoring
- Real-time subscriptions and Edge Functions
- TypeScript type generation
Authentication Setup
Section titled “Authentication Setup”# Add to your environment fileSUPABASE_ACCESS_TOKEN=sb_your_access_token_hereSUPABASE_URL=https://your-project.supabase.coSUPABASE_ANON_KEY=your_anon_key_hereGet your tokens from:
- Access Token: Supabase Account Settings
- Project URL & Keys: Your project’s Settings > API page
Available Functions
Section titled “Available Functions”Project Management
Section titled “Project Management”// List all projectsawait listProjects()
// Get specific project detailsawait getProject({ id: "project-id" })
// Pause/restore projectsawait pauseProject({ project_id: "project-id" })await restoreProject({ project_id: "project-id" })// List tables and schemasawait listTables({project_id: "project-id",schemas: ["public", "auth"]})
// Execute SQL queriesawait executeSql({project_id: "project-id",query: "SELECT * FROM users LIMIT 10"})
// Apply database migrationsawait applyMigration({project_id: "project-id",name: "add_user_profiles",query: "CREATE TABLE user_profiles (id UUID PRIMARY KEY, name TEXT);"})// Generate TypeScript typesawait generateTypescriptTypes({ project_id: "project-id" })
// Get project API configurationawait getProjectUrl({ project_id: "project-id" })await getAnonKey({ project_id: "project-id" })Edge Functions
Section titled “Edge Functions”// List all Edge Functionsawait listEdgeFunctions({ project_id: "project-id" })
// Get function detailsawait getEdgeFunction({project_id: "project-id",function_slug: "hello-world"})
// Deploy new Edge Functionawait deployEdgeFunction({project_id: "project-id",name: "user-profile-handler",files: [ { name: "index.ts", content: ` import { serve } from 'https://deno.land/[email protected]/http/server.ts'
serve(async (req: Request) => { const { name } = await req.json()
return new Response( JSON.stringify({ message: `Hello, ${name}!` }), { headers: { 'Content-Type': 'application/json' } } ) }) ` }]})Development Branches
Section titled “Development Branches”// Create development branchawait createBranch({project_id: "project-id",name: "feature-branch",confirm_cost_id: "cost-confirmation-id"})
// List all branchesawait listBranches({ project_id: "project-id" })
// Merge branch to productionawait mergeBranch({ branch_id: "branch-id" })
// Reset branch to specific migrationawait resetBranch({branch_id: "branch-id",migration_version: "20240101000000"})Usage Examples
Section titled “Usage Examples”Create and manage database schemas:
// Create a new table with RLSawait applyMigration({project_id: "your-project-id",name: "create_posts_table",query: ` CREATE TABLE posts ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, title TEXT NOT NULL, content TEXT, author_id UUID REFERENCES auth.users(id), created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), published BOOLEAN DEFAULT false );
-- Enable Row Level Security ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Create policies CREATE POLICY "Users can view published posts" ON posts FOR SELECT USING (published = true);
CREATE POLICY "Users can manage their own posts" ON posts FOR ALL USING (auth.uid() = author_id);`})
// Verify table creationconst tables = await listTables({project_id: "your-project-id",schemas: ["public"]})Query and manipulate data:
// Insert sample dataawait executeSql({project_id: "your-project-id",query: ` INSERT INTO posts (title, content, author_id, published) VALUES ('Getting Started with Astro', 'Content here...', auth.uid(), true), ('Advanced Astro Patterns', 'More content...', auth.uid(), false);`})
// Query with filtersawait executeSql({project_id: "your-project-id",query: ` SELECT p.*, u.email as author_email FROM posts p JOIN auth.users u ON p.author_id = u.id WHERE p.published = true ORDER BY p.created_at DESC LIMIT 10;`})
// Update dataawait executeSql({project_id: "your-project-id",query: ` UPDATE posts SET published = true WHERE id = 'post-uuid-here';`})Generate TypeScript types for your database:
// Generate types for the entire databaseconst types = await generateTypescriptTypes({project_id: "your-project-id"})
// Save types to file (typically done in your build process)// The generated types will include:export interface Database {public: { Tables: { posts: { Row: { id: string title: string content: string | null author_id: string | null created_at: string | null published: boolean | null } Insert: { id?: string title: string content?: string | null author_id?: string | null created_at?: string | null published?: boolean | null } Update: { id?: string title?: string content?: string | null author_id?: string | null created_at?: string | null published?: boolean | null } } }}}Security & Best Practices
Section titled “Security & Best Practices”RLS Policy Examples
Section titled “RLS Policy Examples”-- User can only read their own profileCREATE POLICY "Users can view own profile" ON profilesFOR SELECT USING (auth.uid() = user_id);
-- Admin users can read all dataCREATE POLICY "Admins can view all" ON profilesFOR SELECT USING ( EXISTS ( SELECT 1 FROM user_roles WHERE user_id = auth.uid() AND role = 'admin' ));
-- Public read, authenticated writeCREATE POLICY "Public read access" ON postsFOR SELECT USING (published = true);
CREATE POLICY "Authenticated write access" ON postsFOR INSERT WITH CHECK (auth.uid() IS NOT NULL);Error Handling
Section titled “Error Handling”try {await executeSql({ project_id: "your-project-id", query: "SELECT * FROM non_existent_table"})} catch (error) {// Handle different error typesif (error.message.includes('relation "non_existent_table" does not exist')) { console.log('Table not found - creating it first')} else if (error.message.includes('permission denied')) { console.log('Check RLS policies and user permissions')} else { console.log('Unexpected database error:', error.message)}}Integration with astro-basics
Section titled “Integration with astro-basics”// Common workflow for astro-basics project// 1. Get current project infoconst project = await getProject({ id: process.env.SUPABASE_PROJECT_ID })
// 2. Check existing tablesconst tables = await listTables({project_id: project.id,schemas: ["public"]})
// 3. Apply new migrations if neededawait applyMigration({project_id: project.id,name: "add_comments_table",query: commentTableSchema})
// 4. Generate updated TypeScript typesconst updatedTypes = await generateTypescriptTypes({project_id: project.id})
// 5. Update environment configurationconst apiUrl = await getProjectUrl({ project_id: project.id })const anonKey = await getAnonKey({ project_id: project.id })Limitations
Section titled “Limitations”- Rate Limits: Subject to Supabase API rate limits (typically 1000 requests/hour for free tier)
- SQL Execution: Some operations require database owner permissions
- Branch Operations: Development branches are a paid feature
- Migration History: Cannot rollback migrations - use branches for testing
Related Resources
Section titled “Related Resources”- Clerk Server - For user authentication integration
- Netlify Server - For deployment with environment variables
- MCP Examples - See database integration examples
Troubleshooting
Section titled “Troubleshooting”Authentication errors:
- Verify your access token has the required permissions
- Check project ID is correct
- Ensure project is not paused
SQL execution errors:
- Verify table and column names are correct
- Check RLS policies if getting permission denied
- Use qualified names (schema.table) when necessary
Type generation issues:
- Ensure you have read permissions on all tables
- Check for circular dependencies in foreign keys
- Verify schema names are correct