MCP/Rules Improvements + MCP Prompts (#357)
- Use ESM for building the MCP Server - Added own Postgres dependency to MCP Server for querying tables and other entities in MCP - Vastly improved AI Agent rules - Added MCP Prompts for reviewing code and planning features - Minor refactoring
This commit is contained in:
committed by
GitHub
parent
f85035bd01
commit
9712e2354b
@@ -36,6 +36,29 @@ Example:
|
||||
- Team server utils: `app/home/[account]/_lib/server/`
|
||||
- Marketing components: `app/(marketing)/_components/`
|
||||
|
||||
The `[account]` parameter is the `accounts.slug` property, not the ID
|
||||
|
||||
## React Server Components - Async Pattern
|
||||
|
||||
**CRITICAL**: In Next.js 15, always await params directly in async server components:
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG - Don't use React.use() in async functions
|
||||
async function Page({ params }: Props) {
|
||||
const { account } = use(params);
|
||||
}
|
||||
|
||||
// ✅ CORRECT - await params directly in Next.js 15
|
||||
async function Page({ params }: Props) {
|
||||
const { account } = await params; // ✅ Server component pattern
|
||||
}
|
||||
|
||||
// ✅ CORRECT - "use" in non-async functions in Next.js 15
|
||||
function Page({ params }: Props) {
|
||||
const { account } = use(params); // ✅ Server component pattern
|
||||
}
|
||||
```
|
||||
|
||||
## Data Fetching Strategy
|
||||
|
||||
**Quick Decision Framework:**
|
||||
@@ -182,7 +205,10 @@ import { Trans } from '@kit/ui/trans';
|
||||
2. Create translation files in `public/locales/[new-language]/`
|
||||
3. Copy structure from English files
|
||||
|
||||
Translation files: `public/locales/<locale>/<namespace>.json`
|
||||
### Adding new namespaces
|
||||
|
||||
1. Translation files: `public/locales/<locale>/<namespace>.json`
|
||||
2. Add namespace to `defaultI18nNamespaces` in `apps/web/lib/i18n/i18n.settings.ts`
|
||||
|
||||
## Workspace Contexts 🏢
|
||||
|
||||
@@ -238,6 +264,55 @@ export const POST = enhanceRouteHandler(
|
||||
);
|
||||
```
|
||||
|
||||
## Navigation Menu Configuration 🗺️
|
||||
|
||||
### Adding Sidebar Menu Items
|
||||
|
||||
**Config Files:**
|
||||
|
||||
- Personal: `config/personal-account-navigation.config.tsx`
|
||||
- Team: `config/team-account-navigation.config.tsx`
|
||||
|
||||
**Add to Personal Navigation:**
|
||||
|
||||
```typescript
|
||||
{
|
||||
label: 'common:routes.yourFeature',
|
||||
path: pathsConfig.app.yourFeaturePath,
|
||||
Icon: <YourIcon className="w-4" />,
|
||||
end: true,
|
||||
},
|
||||
```
|
||||
|
||||
**Add to Team Navigation:**
|
||||
|
||||
```typescript
|
||||
{
|
||||
label: 'common:routes.yourTeamFeature',
|
||||
path: createPath(pathsConfig.app.yourTeamFeaturePath, account),
|
||||
Icon: <YourIcon className="w-4" />,
|
||||
},
|
||||
```
|
||||
|
||||
**Add Paths:**
|
||||
|
||||
```typescript
|
||||
// config/paths.config.ts
|
||||
app: {
|
||||
yourFeaturePath: '/home/your-feature',
|
||||
yourTeamFeaturePath: '/home/[account]/your-feature',
|
||||
}
|
||||
```
|
||||
|
||||
**Add Translations:**
|
||||
|
||||
```json
|
||||
// public/locales/en/common.json
|
||||
"routes": {
|
||||
"yourFeature": "Your Feature"
|
||||
}
|
||||
```
|
||||
|
||||
## Security Guidelines 🛡️
|
||||
|
||||
### Authentication & Authorization
|
||||
@@ -252,13 +327,3 @@ export const POST = enhanceRouteHandler(
|
||||
- **Never pass sensitive data** to Client Components
|
||||
- **Never expose server environment variables** to client (unless prefixed with NEXT_PUBLIC)
|
||||
- Always validate user input
|
||||
|
||||
### Super Admin Protection
|
||||
|
||||
For admin routes, use `AdminGuard` from `@packages/features/admin/src/components/admin-guard.tsx`:
|
||||
|
||||
```tsx
|
||||
import { AdminGuard } from '@kit/admin/components/admin-guard';
|
||||
|
||||
export default AdminGuard(AdminPageComponent);
|
||||
```
|
||||
|
||||
@@ -36,6 +36,29 @@ Example:
|
||||
- Team server utils: `app/home/[account]/_lib/server/`
|
||||
- Marketing components: `app/(marketing)/_components/`
|
||||
|
||||
The `[account]` parameter is the `accounts.slug` property, not the ID
|
||||
|
||||
## React Server Components - Async Pattern
|
||||
|
||||
**CRITICAL**: In Next.js 15, always await params directly in async server components:
|
||||
|
||||
```typescript
|
||||
// ❌ WRONG - Don't use React.use() in async functions
|
||||
async function Page({ params }: Props) {
|
||||
const { account } = use(params);
|
||||
}
|
||||
|
||||
// ✅ CORRECT - await params directly in Next.js 15
|
||||
async function Page({ params }: Props) {
|
||||
const { account } = await params; // ✅ Server component pattern
|
||||
}
|
||||
|
||||
// ✅ CORRECT - "use" in non-async functions in Next.js 15
|
||||
function Page({ params }: Props) {
|
||||
const { account } = use(params); // ✅ Server component pattern
|
||||
}
|
||||
```
|
||||
|
||||
## Data Fetching Strategy
|
||||
|
||||
**Quick Decision Framework:**
|
||||
@@ -182,7 +205,10 @@ import { Trans } from '@kit/ui/trans';
|
||||
2. Create translation files in `public/locales/[new-language]/`
|
||||
3. Copy structure from English files
|
||||
|
||||
Translation files: `public/locales/<locale>/<namespace>.json`
|
||||
### Adding new namespaces
|
||||
|
||||
1. Translation files: `public/locales/<locale>/<namespace>.json`
|
||||
2. Add namespace to `defaultI18nNamespaces` in `apps/web/lib/i18n/i18n.settings.ts`
|
||||
|
||||
## Workspace Contexts 🏢
|
||||
|
||||
@@ -238,6 +264,55 @@ export const POST = enhanceRouteHandler(
|
||||
);
|
||||
```
|
||||
|
||||
## Navigation Menu Configuration 🗺️
|
||||
|
||||
### Adding Sidebar Menu Items
|
||||
|
||||
**Config Files:**
|
||||
|
||||
- Personal: `config/personal-account-navigation.config.tsx`
|
||||
- Team: `config/team-account-navigation.config.tsx`
|
||||
|
||||
**Add to Personal Navigation:**
|
||||
|
||||
```typescript
|
||||
{
|
||||
label: 'common:routes.yourFeature',
|
||||
path: pathsConfig.app.yourFeaturePath,
|
||||
Icon: <YourIcon className="w-4" />,
|
||||
end: true,
|
||||
},
|
||||
```
|
||||
|
||||
**Add to Team Navigation:**
|
||||
|
||||
```typescript
|
||||
{
|
||||
label: 'common:routes.yourTeamFeature',
|
||||
path: createPath(pathsConfig.app.yourTeamFeaturePath, account),
|
||||
Icon: <YourIcon className="w-4" />,
|
||||
},
|
||||
```
|
||||
|
||||
**Add Paths:**
|
||||
|
||||
```typescript
|
||||
// config/paths.config.ts
|
||||
app: {
|
||||
yourFeaturePath: '/home/your-feature',
|
||||
yourTeamFeaturePath: '/home/[account]/your-feature',
|
||||
}
|
||||
```
|
||||
|
||||
**Add Translations:**
|
||||
|
||||
```json
|
||||
// public/locales/en/common.json
|
||||
"routes": {
|
||||
"yourFeature": "Your Feature"
|
||||
}
|
||||
```
|
||||
|
||||
## Security Guidelines 🛡️
|
||||
|
||||
### Authentication & Authorization
|
||||
@@ -252,13 +327,3 @@ export const POST = enhanceRouteHandler(
|
||||
- **Never pass sensitive data** to Client Components
|
||||
- **Never expose server environment variables** to client (unless prefixed with NEXT_PUBLIC)
|
||||
- Always validate user input
|
||||
|
||||
### Super Admin Protection
|
||||
|
||||
For admin routes, use `AdminGuard` from `@packages/features/admin/src/components/admin-guard.tsx`:
|
||||
|
||||
```tsx
|
||||
import { AdminGuard } from '@kit/admin/components/admin-guard';
|
||||
|
||||
export default AdminGuard(AdminPageComponent);
|
||||
```
|
||||
|
||||
119
apps/web/app/admin/AGENTS.md
Normal file
119
apps/web/app/admin/AGENTS.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Super Admin
|
||||
|
||||
This file provides specific guidance for AI agents working in the super admin section of the application.
|
||||
|
||||
## Core Admin Principles
|
||||
|
||||
### Security-First Development
|
||||
|
||||
- **ALWAYS** use `AdminGuard` to protect admin pages
|
||||
- **NEVER** bypass authentication or authorization checks
|
||||
- **CRITICAL**: Use admin Supabase client with manual authorization validation
|
||||
- Validate permissions for every admin operation
|
||||
|
||||
### Admin Client Usage Pattern
|
||||
|
||||
```typescript
|
||||
import { isSuperAdmin } from '@kit/admin';
|
||||
import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client';
|
||||
|
||||
async function adminOperation() {
|
||||
const adminClient = getSupabaseServerAdminClient();
|
||||
|
||||
// CRITICAL: Always validate admin status first
|
||||
const currentUser = await getCurrentUser();
|
||||
if (!(await isSuperAdmin(currentUser))) {
|
||||
throw new Error('Unauthorized: Admin access required');
|
||||
}
|
||||
|
||||
// Now safe to proceed with admin privileges
|
||||
const { data } = await adminClient.from('accounts').select('*');
|
||||
return data;
|
||||
}
|
||||
```
|
||||
|
||||
## Page Structure Patterns
|
||||
|
||||
### Standard Admin Page Template
|
||||
|
||||
```typescript
|
||||
import { AdminGuard } from '@kit/admin/components/admin-guard';
|
||||
import { PageBody, PageHeader } from '@kit/ui/page';
|
||||
import { AppBreadcrumbs } from '@kit/ui/app-breadcrumbs';
|
||||
|
||||
async function AdminPageComponent() {
|
||||
return (
|
||||
<>
|
||||
<PageHeader description={<AppBreadcrumbs />}>
|
||||
{/* Page actions go here */}
|
||||
</PageHeader>
|
||||
|
||||
<PageBody>
|
||||
{/* Main content */}
|
||||
</PageBody>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// ALWAYS wrap with AdminGuard
|
||||
export default AdminGuard(AdminPageComponent);
|
||||
```
|
||||
|
||||
### Async Server Component Pattern
|
||||
|
||||
```typescript
|
||||
// ✅ CORRECT - Next.js 15 pattern
|
||||
async function AdminPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params; // ✅ await params directly
|
||||
|
||||
// Fetch admin data
|
||||
const data = await loadAdminData(id);
|
||||
|
||||
return <AdminContent data={data} />;
|
||||
}
|
||||
```
|
||||
|
||||
## Security Guidelines
|
||||
|
||||
### Critical Security Rules
|
||||
|
||||
1. **NEVER** expose admin functionality to non-admin users
|
||||
2. **ALWAYS** validate admin status before operations
|
||||
3. **NEVER** trust client-side admin checks alone
|
||||
4. **ALWAYS** use server-side validation for admin actions
|
||||
5. **NEVER** log sensitive admin data
|
||||
6. **ALWAYS** audit admin operations
|
||||
|
||||
### Admin Action Auditing
|
||||
|
||||
```typescript
|
||||
async function auditedAdminAction(action: string, data: unknown) {
|
||||
const logger = await getLogger();
|
||||
|
||||
await logger.info(
|
||||
{
|
||||
name: 'admin-audit',
|
||||
action,
|
||||
adminId: currentUser.id,
|
||||
timestamp: new Date().toISOString(),
|
||||
data: {
|
||||
// Log only non-sensitive fields
|
||||
operation: action,
|
||||
targetId: data.id,
|
||||
},
|
||||
},
|
||||
'Admin action performed',
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Common Patterns to Follow
|
||||
|
||||
1. **Always wrap admin pages with `AdminGuard`**
|
||||
2. **Use admin client only when RLS bypass is required**
|
||||
3. **Implement proper error boundaries for admin components**
|
||||
4. **Add comprehensive logging for admin operations**
|
||||
5. **Use TypeScript strictly for admin interfaces**
|
||||
6. **Follow the established admin component naming conventions**
|
||||
7. **Implement proper loading states for admin operations**
|
||||
8. **Add proper metadata to admin pages**
|
||||
119
apps/web/app/admin/CLAUDE.md
Normal file
119
apps/web/app/admin/CLAUDE.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Super Admin
|
||||
|
||||
This file provides specific guidance for AI agents working in the super admin section of the application.
|
||||
|
||||
## Core Admin Principles
|
||||
|
||||
### Security-First Development
|
||||
|
||||
- **ALWAYS** use `AdminGuard` to protect admin pages
|
||||
- **NEVER** bypass authentication or authorization checks
|
||||
- **CRITICAL**: Use admin Supabase client with manual authorization validation
|
||||
- Validate permissions for every admin operation
|
||||
|
||||
### Admin Client Usage Pattern
|
||||
|
||||
```typescript
|
||||
import { isSuperAdmin } from '@kit/admin';
|
||||
import { getSupabaseServerAdminClient } from '@kit/supabase/server-admin-client';
|
||||
|
||||
async function adminOperation() {
|
||||
const adminClient = getSupabaseServerAdminClient();
|
||||
|
||||
// CRITICAL: Always validate admin status first
|
||||
const currentUser = await getCurrentUser();
|
||||
if (!(await isSuperAdmin(currentUser))) {
|
||||
throw new Error('Unauthorized: Admin access required');
|
||||
}
|
||||
|
||||
// Now safe to proceed with admin privileges
|
||||
const { data } = await adminClient.from('accounts').select('*');
|
||||
return data;
|
||||
}
|
||||
```
|
||||
|
||||
## Page Structure Patterns
|
||||
|
||||
### Standard Admin Page Template
|
||||
|
||||
```typescript
|
||||
import { AdminGuard } from '@kit/admin/components/admin-guard';
|
||||
import { PageBody, PageHeader } from '@kit/ui/page';
|
||||
import { AppBreadcrumbs } from '@kit/ui/app-breadcrumbs';
|
||||
|
||||
async function AdminPageComponent() {
|
||||
return (
|
||||
<>
|
||||
<PageHeader description={<AppBreadcrumbs />}>
|
||||
{/* Page actions go here */}
|
||||
</PageHeader>
|
||||
|
||||
<PageBody>
|
||||
{/* Main content */}
|
||||
</PageBody>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// ALWAYS wrap with AdminGuard
|
||||
export default AdminGuard(AdminPageComponent);
|
||||
```
|
||||
|
||||
### Async Server Component Pattern
|
||||
|
||||
```typescript
|
||||
// ✅ CORRECT - Next.js 15 pattern
|
||||
async function AdminPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params; // ✅ await params directly
|
||||
|
||||
// Fetch admin data
|
||||
const data = await loadAdminData(id);
|
||||
|
||||
return <AdminContent data={data} />;
|
||||
}
|
||||
```
|
||||
|
||||
## Security Guidelines
|
||||
|
||||
### Critical Security Rules
|
||||
|
||||
1. **NEVER** expose admin functionality to non-admin users
|
||||
2. **ALWAYS** validate admin status before operations
|
||||
3. **NEVER** trust client-side admin checks alone
|
||||
4. **ALWAYS** use server-side validation for admin actions
|
||||
5. **NEVER** log sensitive admin data
|
||||
6. **ALWAYS** audit admin operations
|
||||
|
||||
### Admin Action Auditing
|
||||
|
||||
```typescript
|
||||
async function auditedAdminAction(action: string, data: unknown) {
|
||||
const logger = await getLogger();
|
||||
|
||||
await logger.info(
|
||||
{
|
||||
name: 'admin-audit',
|
||||
action,
|
||||
adminId: currentUser.id,
|
||||
timestamp: new Date().toISOString(),
|
||||
data: {
|
||||
// Log only non-sensitive fields
|
||||
operation: action,
|
||||
targetId: data.id,
|
||||
},
|
||||
},
|
||||
'Admin action performed',
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Common Patterns to Follow
|
||||
|
||||
1. **Always wrap admin pages with `AdminGuard`**
|
||||
2. **Use admin client only when RLS bypass is required**
|
||||
3. **Implement proper error boundaries for admin components**
|
||||
4. **Add comprehensive logging for admin operations**
|
||||
5. **Use TypeScript strictly for admin interfaces**
|
||||
6. **Follow the established admin component naming conventions**
|
||||
7. **Implement proper loading states for admin operations**
|
||||
8. **Add proper metadata to admin pages**
|
||||
@@ -6,13 +6,15 @@ This file contains guidance for working with database schemas, migrations, and S
|
||||
|
||||
Schemas are organized in numbered files in the `schemas/` directory. Numbers are used to sort dependencies.
|
||||
|
||||
## Schema Development Workflow
|
||||
Migrations are generated from schemas. If creating a new schema, the migration can be created using the exact same content.
|
||||
|
||||
If modifying an existing migration, use the `diff` command:
|
||||
|
||||
### 1. Creating New Schema Files
|
||||
|
||||
```bash
|
||||
# Create new schema file
|
||||
touch schemas/15-my-new-feature.sql
|
||||
touch apps/web/supabase/schemas/15-my-new-feature.sql
|
||||
|
||||
# Apply changes and create migration
|
||||
pnpm --filter web run supabase:db:diff -f my-new-feature
|
||||
@@ -24,6 +26,8 @@ pnpm supabase:web:reset
|
||||
pnpm supabase:web:typegen
|
||||
```
|
||||
|
||||
Verify the diff command generated the same content as the schema; if not, take steps to fix the migration.
|
||||
|
||||
### 2. Modifying Existing Schemas
|
||||
|
||||
```bash
|
||||
@@ -35,6 +39,8 @@ pnpm --filter web run supabase:db:diff -f update-accounts
|
||||
|
||||
# Apply and test
|
||||
pnpm supabase:web:reset
|
||||
|
||||
# After resetting
|
||||
pnpm supabase:web:typegen
|
||||
```
|
||||
|
||||
@@ -223,47 +229,6 @@ pnpm supabase:web:reset
|
||||
pnpm run supabase:web:test
|
||||
```
|
||||
|
||||
## Type Generation
|
||||
|
||||
### After Schema Changes
|
||||
|
||||
```bash
|
||||
# Generate types after any schema changes
|
||||
pnpm supabase:web:typegen
|
||||
# Types are generated to src/lib/supabase/database.types.ts
|
||||
|
||||
# Reset DB
|
||||
pnpm supabase:web:reset
|
||||
```
|
||||
|
||||
### Using Generated Types
|
||||
|
||||
```typescript
|
||||
import { Enums, Tables } from '@kit/supabase/database';
|
||||
|
||||
// Table types
|
||||
type Account = Tables<'accounts'>;
|
||||
type Note = Tables<'notes'>;
|
||||
|
||||
// Enum types
|
||||
type AppPermission = Enums<'app_permissions'>;
|
||||
|
||||
// Insert types
|
||||
type AccountInsert = Tables<'accounts'>['Insert'];
|
||||
type AccountUpdate = Tables<'accounts'>['Update'];
|
||||
|
||||
// Use in functions
|
||||
async function createNote(data: Tables<'notes'>['Insert']) {
|
||||
const { data: note, error } = await supabase
|
||||
.from('notes')
|
||||
.insert(data)
|
||||
.select()
|
||||
.single();
|
||||
|
||||
return note;
|
||||
}
|
||||
```
|
||||
|
||||
## Common Schema Patterns
|
||||
|
||||
### Audit Trail
|
||||
|
||||
@@ -6,13 +6,15 @@ This file contains guidance for working with database schemas, migrations, and S
|
||||
|
||||
Schemas are organized in numbered files in the `schemas/` directory. Numbers are used to sort dependencies.
|
||||
|
||||
## Schema Development Workflow
|
||||
Migrations are generated from schemas. If creating a new schema, the migration can be created using the exact same content.
|
||||
|
||||
If modifying an existing migration, use the `diff` command:
|
||||
|
||||
### 1. Creating New Schema Files
|
||||
|
||||
```bash
|
||||
# Create new schema file
|
||||
touch schemas/15-my-new-feature.sql
|
||||
touch apps/web/supabase/schemas/15-my-new-feature.sql
|
||||
|
||||
# Apply changes and create migration
|
||||
pnpm --filter web run supabase:db:diff -f my-new-feature
|
||||
@@ -24,6 +26,8 @@ pnpm supabase:web:reset
|
||||
pnpm supabase:web:typegen
|
||||
```
|
||||
|
||||
Verify the diff command generated the same content as the schema; if not, take steps to fix the migration.
|
||||
|
||||
### 2. Modifying Existing Schemas
|
||||
|
||||
```bash
|
||||
@@ -35,6 +39,8 @@ pnpm --filter web run supabase:db:diff -f update-accounts
|
||||
|
||||
# Apply and test
|
||||
pnpm supabase:web:reset
|
||||
|
||||
# After resetting
|
||||
pnpm supabase:web:typegen
|
||||
```
|
||||
|
||||
@@ -223,47 +229,6 @@ pnpm supabase:web:reset
|
||||
pnpm run supabase:web:test
|
||||
```
|
||||
|
||||
## Type Generation
|
||||
|
||||
### After Schema Changes
|
||||
|
||||
```bash
|
||||
# Generate types after any schema changes
|
||||
pnpm supabase:web:typegen
|
||||
# Types are generated to src/lib/supabase/database.types.ts
|
||||
|
||||
# Reset DB
|
||||
pnpm supabase:web:reset
|
||||
```
|
||||
|
||||
### Using Generated Types
|
||||
|
||||
```typescript
|
||||
import { Enums, Tables } from '@kit/supabase/database';
|
||||
|
||||
// Table types
|
||||
type Account = Tables<'accounts'>;
|
||||
type Note = Tables<'notes'>;
|
||||
|
||||
// Enum types
|
||||
type AppPermission = Enums<'app_permissions'>;
|
||||
|
||||
// Insert types
|
||||
type AccountInsert = Tables<'accounts'>['Insert'];
|
||||
type AccountUpdate = Tables<'accounts'>['Update'];
|
||||
|
||||
// Use in functions
|
||||
async function createNote(data: Tables<'notes'>['Insert']) {
|
||||
const { data: note, error } = await supabase
|
||||
.from('notes')
|
||||
.insert(data)
|
||||
.select()
|
||||
.single();
|
||||
|
||||
return note;
|
||||
}
|
||||
```
|
||||
|
||||
## Common Schema Patterns
|
||||
|
||||
### Audit Trail
|
||||
|
||||
Reference in New Issue
Block a user