Comprehensive guide for managing multi-institution architecture in JKKN websites. This skill should be used when: - Adding a new institution to the platform - Creating or syncing database migrations across institutions - Setting up Supabase projects for new institutions - Configuring Vercel deployments for institutions - Managing environment variables and feature flags - Troubleshooting multi-institution issues - Understanding the Single Repository, Multiple Deployments (SRMD) architecture - Applying database schema to new or existing institution Supabase projects - Running local development server for a specific institution Automatically triggers when user mentions: "add institution", "new institution", "sync migrations", "multi-tenant", "setup supabase project", "setup vercel", "institution deployment", "switch institution", "dev:main", "dev:dental", or asks about managing multiple JKKN websites.
This skill provides comprehensive guidance for managing the JKKN multi-institution architecture where ONE codebase serves SIX institution websites through separate Supabase and Vercel deployments.
Each Supabase project has its own dedicated MCP server. You MUST use the correct MCP tool prefix for the target institution:
| Institution | Supabase Project ID | MCP Tool Prefix |
|---|---|---|
| Main | pmqodbfhsejbvfbmsfeq | mcp__Main_Supabase_Project__ |
| Dental College | wnmyvbnqldukeknnmnpl | mcp__Dental_College_Supabase_Project__ |
| Pharmacy College | rwskookarbolpmtolqkd | mcp__Pharmacy_College_Supabase_Project__ |
| Engineering College | kyvfkyjmdbtyimtedkie |
mcp__Engineering_College_Supabase_Project__ |
| Arts & Science | TBD | TBD |
| Nursing | TBD | TBD |
Each institution's MCP server provides these tools:
mcp__[Institution]_Supabase_Project__list_tables
mcp__[Institution]_Supabase_Project__execute_sql
mcp__[Institution]_Supabase_Project__apply_migration
mcp__[Institution]_Supabase_Project__list_migrations
mcp__[Institution]_Supabase_Project__generate_typescript_types
mcp__[Institution]_Supabase_Project__get_logs
mcp__[Institution]_Supabase_Project__get_advisors
mcp__[Institution]_Supabase_Project__get_project_url
mcp__[Institution]_Supabase_Project__get_publishable_keys
mcp__[Institution]_Supabase_Project__search_docs
// Step 1: Apply to Main Supabase
mcp__Main_Supabase_Project__apply_migration({
name: "add_new_feature_table",
query: "CREATE TABLE ..."
})
// Step 2: Apply to Dental College Supabase
mcp__Dental_College_Supabase_Project__apply_migration({
name: "add_new_feature_table",
query: "CREATE TABLE ..." // Same SQL
})
// Step 3: Repeat for other institutions when their MCP servers are configured
ALL database schema, functions, RLS policies, and triggers are documented in docs/database/.
docs/database/
├── main-supabase/ # Main Supabase project (pmqodbfhsejbvfbmsfeq)
│ ├── README.md # Documentation guide
│ ├── 01-functions.sql # All PostgreSQL functions (51+ functions)
│ ├── 02-rls-policies.sql # All RLS policies (126+ policies)
│ ├── 03-triggers.sql # All triggers
│ ├── 04-foreign-keys.sql # All foreign key relationships
│ └── 05-migrations/ # Migration history
├── dental-supabase/ # Dental College project
│ └── [same structure]
└── [other-institution]/ # Future institution projects
└── [same structure]
BEFORE executing ANY database migration, you MUST:
docs/database/main-supabase/| Change Type | Documentation File | Example |
|---|---|---|
| New function | 01-functions.sql | is_super_admin(), get_dashboard_stats() |
| New RLS policy | 02-rls-policies.sql | User roles SELECT policy |
| New trigger | 03-triggers.sql | handle_new_user() trigger |
| New foreign key | 04-foreign-keys.sql | user_roles.user_id → auth.users.id |
| New table | All files as needed | Create table + RLS + triggers |
When adding SQL to documentation files, use this format:
-- ============================================
-- Function/Policy/Trigger Name
-- ============================================
-- Purpose: [Brief description]
-- Created: [Date]
-- Modified: [Date if updated]
-- Dependencies: [List any dependencies]
-- Used by: [List tables/functions that use this]
-- Security: [SECURITY DEFINER / INVOKER if applicable]
-- ============================================
[SQL CODE HERE]
-- End of [Name]
-- ============================================
When setting up a new institution's Supabase project, follow this workflow to create the complete backend:
// Read all SQL documentation files
Read({ file_path: "docs/database/main-supabase/01-functions.sql" })
Read({ file_path: "docs/database/main-supabase/02-rls-policies.sql" })
Read({ file_path: "docs/database/main-supabase/03-triggers.sql" })
Read({ file_path: "docs/database/main-supabase/04-foreign-keys.sql" })
Functions must be created before RLS policies that reference them:
// Apply all functions from 01-functions.sql
mcp__[New_Institution]_Supabase_Project__apply_migration({
name: "001_create_functions",
query: "[SQL from 01-functions.sql]"
})
Create all tables (get from existing migrations or documentation):
mcp__[New_Institution]_Supabase_Project__apply_migration({
name: "002_create_tables",
query: "[Table creation SQL]"
})
mcp__[New_Institution]_Supabase_Project__apply_migration({
name: "003_create_rls_policies",
query: "[SQL from 02-rls-policies.sql]"
})
mcp__[New_Institution]_Supabase_Project__apply_migration({
name: "004_create_triggers",
query: "[SQL from 03-triggers.sql]"
})
// List all tables to verify
mcp__[New_Institution]_Supabase_Project__list_tables()
// Check for security advisories
mcp__[New_Institution]_Supabase_Project__get_advisors({ type: "security" })
The following functions are critical for the permission system:
| Function | Purpose |
|---|---|
is_super_admin(user_uuid) | Check if user has super_admin role |
has_permission(user_uuid, permission) | Check if user has specific permission |
has_any_role(user_uuid, role_names[]) | Check if user has any of the specified roles |
get_user_roles(user_uuid) | Get all roles assigned to a user |
get_role_permissions(role_id) | Get all permissions for a role |
| Function | Purpose |
|---|---|
get_dashboard_stats() | Get dashboard statistics |
get_user_activity_summary(user_uuid) | Get user activity metrics |
| Function | Purpose |
|---|---|
handle_new_user() | Trigger function for new user creation |
is_email_approved(email) | Check if email is in approved list |
Pattern 1: Public Read, Admin Write
-- Public can view
CREATE POLICY "Public can view published"
ON table_name FOR SELECT
USING (status = 'published');
-- Admins can manage
CREATE POLICY "Admins can manage"
ON table_name FOR ALL
TO authenticated
USING (is_super_admin(auth.uid()));
Pattern 2: Owner-Based Access
-- Users can manage their own
CREATE POLICY "Users can manage own"
ON table_name FOR ALL
TO authenticated
USING (created_by = auth.uid());
Pattern 3: Permission-Based Access
-- Users with specific permission
CREATE POLICY "Users with permission can manage"
ON table_name FOR ALL
TO authenticated
USING (has_permission(auth.uid(), 'module:resource:action'));
# Start development with Main Institution
npm run dev:main
# Start development with Dental College
npm run dev:dental
# Switch without starting server
npm run switch main
npm run switch dental
The scripts/switch-institution.ts script:
.env.local with correct Supabase credentialsEdit scripts/switch-institution.ts:
const INSTITUTIONS: Record<string, InstitutionEnv> = {
main: { /* existing */ },
dental: { /* existing */ },
// Add new institution:
'arts-science': {
id: 'arts-science',
name: 'JKKN Arts & Science College',
supabaseUrl: 'https://xxx.supabase.co',
supabaseAnonKey: 'eyJ...',
siteUrl: 'http://localhost:3000',
features: 'blog,careers,page-builder,analytics',
},
};
For admin operations requiring service role key:
.env.[institution-id].servicekey file (gitignored).env.local┌─────────────────────────────────────────────────────────────────┐
│ ONE GitHub Repository │
│ (jkkn-institution-website) │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Vercel: Main │ │ Vercel: Dental│ │ Vercel: Arts │
│ jkkn.ac.in │ │ dental.jkkn. │ │ arts.jkkn. │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Supabase Main │ │Supabase Dental│ │ Supabase Arts │
│ MCP: Main_... │ │ MCP: Dental...│ │ MCP: TBD │
└───────────────┘ └───────────────┘ └───────────────┘
| ID | Name | Domain | Supabase Project | MCP Available |
|---|---|---|---|---|
main | JKKN Institutions | jkkn.ac.in | pmqodbfhsejbvfbmsfeq | Yes |
dental | JKKN Dental College | dental.jkkn.ac.in | wnmyvbnqldukeknnmnpl | Yes |
pharmacy | JKKN Pharmacy | pharmacy.jkkn.ac.in | rwskookarbolpmtolqkd | Yes |
engineering | JKKN Engineering | engg.jkkn.ac.in | kyvfkyjmdbtyimtedkie | Yes |
arts-science | JKKN Arts & Science | arts.jkkn.ac.in | TBD | No |
nursing | JKKN Nursing | nursing.jkkn.ac.in | TBD | No |
Before starting, ensure:
Navigate to Supabase Dashboard:
https://supabase.com/dashboard/projects
Create New Project:
jkkn-[institution-id] (e.g., jkkn-dental)ap-south-1 (Mumbai) or ap-southeast-1 (Singapore)Wait for Project Setup:
abcdefghijklmnop)Get API Credentials:
Project URL, anon/public key, service_role keyAdd the new institution's Supabase MCP server to Claude Code settings:
{
"mcpServers": {
"[Institution]_Supabase_Project": {
"command": "npx",
"args": ["-y", "@anthropic/mcp-supabase", "--project-ref", "[project-ref]"]
}
}
}
Read and apply from docs/database/main-supabase/:
// 1. Read the source of truth
Read({ file_path: "docs/database/main-supabase/01-functions.sql" })
Read({ file_path: "docs/database/main-supabase/02-rls-policies.sql" })
// 2. Apply functions first (required for RLS policies)
mcp__[New_Institution]_Supabase_Project__apply_migration({
name: "001_core_functions",
query: "[Functions SQL from documentation]"
})
// 3. Create tables from existing migrations
mcp__[New_Institution]_Supabase_Project__list_migrations() // Check what exists
// 4. Apply RLS policies
mcp__[New_Institution]_Supabase_Project__apply_migration({
name: "002_rls_policies",
query: "[RLS SQL from documentation]"
})
// 5. Verify
mcp__[New_Institution]_Supabase_Project__list_tables()
mcp__[New_Institution]_Supabase_Project__get_advisors({ type: "security" })
Enable Auth Providers:
https://[domain]/auth/callbackConfigure Auth Settings:
https://[institution-domain]Set up Storage Buckets:
INSERT INTO storage.buckets (id, name, public) VALUES
('avatars', 'avatars', true),
('media', 'media', true),
('resumes', 'resumes', false);
Navigate to Vercel Dashboard:
https://vercel.com/dashboard
Import Git Repository:
jkkn-institution-website repositoryConfigure Project:
jkkn-[institution-id]./ (default)npm run build (default)npm install (default)Add Environment Variables:
Required variables:
NEXT_PUBLIC_INSTITUTION_ID=[institution-id]
NEXT_PUBLIC_INSTITUTION_NAME=[Full Institution Name]
NEXT_PUBLIC_SITE_URL=https://[domain]
NEXT_PUBLIC_SUPABASE_URL=https://[project-ref].supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=[anon-key]
SUPABASE_SERVICE_ROLE_KEY=[service-role-key]
NEXT_PUBLIC_FEATURES=blog,careers,page-builder,analytics
Deploy:
In Vercel Project Settings → Domains:
[institution].jkkn.ac.inConfigure DNS:
[institution] → cname.vercel-dns.comWait for SSL:
Add to scripts/switch-institution.ts:
'[institution-id]': {
id: '[institution-id]',
name: '[Full Institution Name]',
supabaseUrl: 'https://[project-ref].supabase.co',
supabaseAnonKey: '[anon-key]',
siteUrl: 'http://localhost:3000',
features: '[feature-list]',
},
lib/config/multi-tenant.tsWhen making database changes:
docs/database/main-supabase/ firstmcp__Main_Supabase_Project__apply_migration({
name: "descriptive_name",
query: "[SQL from documentation]"
})
mcp__Dental_College_Supabase_Project__apply_migration({
name: "descriptive_name",
query: "[Same SQL]"
})
docs/database/ firstmcp__Main_Supabase_Project__generate_typescript_types()
// Main Supabase
mcp__Main_Supabase_Project__list_migrations()
// Dental College
mcp__Dental_College_Supabase_Project__list_migrations()
| Feature | Description |
|---|---|
blog | Blog system with posts, categories, tags |
careers | Job listings and applications |
page-builder | Visual page editor |
analytics | Traffic and engagement analytics |
comments | User comments on blog posts |
newsletter | Email subscription system |
events | Event management and calendar |
gallery | Photo/video gallery |
testimonials | Testimonial management |
admissions | Admission portal |
faculty-directory | Faculty profiles |
course-catalog | Course listings |
research-publications | Research paper management |
placements | Placement statistics |
Via Environment Variable:
NEXT_PUBLIC_FEATURES=blog,careers,page-builder,analytics
In Code:
import { hasFeature } from '@/lib/config/multi-tenant'
if (hasFeature('blog')) {
// Blog-specific code
}
In Components:
import { FeatureGate } from '@/lib/hooks/use-institution'
<FeatureGate feature="blog">
<BlogSection />
</FeatureGate>
1. Migration sync fails:
2. MCP tool not found:
3. Schema mismatch between institutions:
mcp__Main_Supabase_Project__list_tables() vs mcp__Dental_College_Supabase_Project__list_tables()4. Wrong content showing in development:
npm run dev:main vs npm run dev:dental).env.local has correct NEXT_PUBLIC_INSTITUTION_ID5. RLS policies blocking access:
mcp__[Institution]__execute_sql({ query: "SELECT * FROM pg_proc WHERE proname = 'is_super_admin'" })mcp__[Institution]__get_logs({ service: "auth" })// Check Supabase connection
mcp__Main_Supabase_Project__get_project_url()
mcp__Dental_College_Supabase_Project__get_project_url()
// Check for security issues
mcp__Main_Supabase_Project__get_advisors({ type: "security" })
// Check tables exist
mcp__Main_Supabase_Project__list_tables()
// Check migrations applied
mcp__Main_Supabase_Project__list_migrations()
| File | Purpose |
|---|---|
lib/config/multi-tenant.ts | Institution registry, feature flags |
lib/hooks/use-institution.ts | React hooks for institution context |
scripts/switch-institution.ts | Local dev institution switcher |
docs/database/main-supabase/*.sql | Source of truth for database schema |
docs/MULTI-INSTITUTION-ARCHITECTURE.md | Full architecture docs |
CLAUDE.md | Project documentation with multi-institution section |
# Local Development
npm run dev:main # Start with Main Institution
npm run dev:dental # Start with Dental College
npm run switch main # Switch to Main (no server start)
npm run switch dental # Switch to Dental (no server start)
// MCP Tools - Main Supabase
mcp__Main_Supabase_Project__list_tables()
mcp__Main_Supabase_Project__apply_migration({ name: "...", query: "..." })
mcp__Main_Supabase_Project__execute_sql({ query: "..." })
// MCP Tools - Dental College Supabase
mcp__Dental_College_Supabase_Project__list_tables()
mcp__Dental_College_Supabase_Project__apply_migration({ name: "...", query: "..." })
mcp__Dental_College_Supabase_Project__execute_sql({ query: "..." })
For detailed information, see:
docs/database/main-supabase/README.md - Database documentation guidereferences/environment-variables.md - Complete env var referencereferences/migration-guide.md - Detailed migration proceduresreferences/institution-checklist.md - Full setup checklist