import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; interface PromptTemplate { name: string; title: string; description: string; category: | 'code-review' | 'development' | 'database' | 'testing' | 'architecture' | 'debugging'; arguments: Array<{ name: string; description: string; required: boolean; type: 'string' | 'text' | 'enum'; options?: string[]; }>; template: string; examples?: string[]; } export class PromptsManager { private static prompts: PromptTemplate[] = [ { name: 'code_review', title: 'Comprehensive Code Review', description: 'Analyze code for quality, security, performance, and best practices', category: 'code-review', arguments: [ { name: 'code', description: 'The code to review', required: true, type: 'text', }, { name: 'focus_area', description: 'Specific area to focus the review on', required: false, type: 'enum', options: [ 'security', 'performance', 'maintainability', 'typescript', 'react', 'all', ], }, { name: 'severity_level', description: 'Minimum severity level for issues to report', required: false, type: 'enum', options: ['low', 'medium', 'high', 'critical'], }, ], template: `Please review the following code with a focus on {{focus_area || 'all aspects'}}. **Code to Review:** \`\`\` {{code}} \`\`\` **Makerkit Standards Review Criteria:** **TypeScript Excellence:** - Strict TypeScript with no 'any' types - use explicit types always - Implicit type inference preferred unless impossible - Proper error handling with try/catch and typed error objects - Clean, clear, well-designed code without obvious comments **React & Next.js 15 Best Practices:** - Functional components only with 'use client' directive for client components - Encapsulate repeated blocks of code into reusable local components - Avoid useEffect (code smell) - justify if absolutely necessary - Single state objects over multiple useState calls - Prefer server-side data fetching using React Server Components - Display loading indicators with LoadingSpinner component where appropriate - Add data-test attributes for E2E testing where appropriate - Server actions that redirect should handle the error using "isRedirectError" from 'next/dist/client/components/redirect-error' **Makerkit Architecture Patterns:** - Multi-tenant architecture with proper account-based access control - Use account_id foreign keys for data association - Personal vs Team accounts pattern implementation - Proper use of Row Level Security (RLS) policies - Supabase integration best practices **Database Best Practices:** - Use existing database functions instead of writing your own - RLS are applied to all tables unless explicitly instructed otherwise - RLS prevents data leakage between accounts - User is prevented from updating fields that are not allowed to be updated (uses column-level permissions) - Triggers for tracking timestamps and user tracking are used if required - Schema is thorough and covers all data integrity and business rules, but is not unnecessarily complex or over-engineered - Schema uses constraints/triggers where required for data integrity and business rules - Schema prevents invalid data from being inserted or updated **Code Quality Standards:** - No unnecessary complexity or overly abstract code - Consistent file structure following monorepo patterns - Proper package organization in Turborepo structure - Use of @kit/ui components and established patterns {{#if severity_level}} **Severity Filter:** Only report issues of {{severity_level}} severity or higher. {{/if}} **Please provide:** 1. **Overview:** Brief summary of code quality 2. **Issues Found:** List specific problems with severity levels 3. **Suggestions:** Concrete improvement recommendations 4. **Best Practices:** Relevant patterns from the Makerkit codebase 5. **Security Review:** Any security concerns or improvements`, examples: [ 'Review a React component for best practices', 'Security-focused review of authentication code', 'Performance analysis of database queries', ], }, { name: 'supabase_rls_policy_design', title: 'Supabase RLS Policy Design', description: 'Design Row Level Security policies for Makerkit multi-tenant architecture', category: 'database', arguments: [ { name: 'table_name', description: 'Table that needs RLS policies', required: true, type: 'string', }, { name: 'access_patterns', description: 'Who should access this data and how', required: true, type: 'text', }, { name: 'data_sensitivity', description: 'Sensitivity level of the data', required: true, type: 'enum', options: [ 'public', 'account-restricted', 'role-restricted', 'owner-only', ], }, ], template: `Design RLS policies for table: {{table_name}} **Access Requirements:** {{access_patterns}} **Data Sensitivity:** {{data_sensitivity}} **Please provide:** **1. Policy Design:** - Complete RLS policy definitions (SELECT, INSERT, UPDATE, DELETE) - Use of existing Makerkit functions: has_role_on_account, has_permission - Account-based access control following multi-tenant patterns **2. Security Analysis:** - How policies enforce account boundaries - Role-based access control integration - Prevention of data leakage between accounts **3. Performance Considerations:** - Index requirements for efficient policy execution - Query optimization with RLS overhead - Use of SECURITY DEFINER functions where needed **4. Policy SQL:** \`\`\`sql -- Enable RLS ALTER TABLE {{table_name}} ENABLE ROW LEVEL SECURITY; -- Your policies here \`\`\` **5. Testing Strategy:** - Test cases for different user roles and permissions - Verification of account isolation - Performance testing with large datasets **Makerkit RLS Standards:** - All user data must respect account boundaries - Use existing permission functions for consistency - Personal accounts: auth.users.id = accounts.id - Team accounts: check via accounts_memberships table - Leverage roles and role_permissions for granular access`, examples: [ 'Design RLS for a documents table', 'Create policies for team collaboration data', 'Set up RLS for billing and subscription data', ], }, ]; static getAllPrompts(): PromptTemplate[] { return this.prompts; } static getPromptsByCategory(category: string): PromptTemplate[] { return this.prompts.filter((prompt) => prompt.category === category); } static getPrompt(name: string): PromptTemplate | null { return this.prompts.find((prompt) => prompt.name === name) || null; } static searchPrompts(query: string): PromptTemplate[] { const searchTerm = query.toLowerCase(); return this.prompts.filter( (prompt) => prompt.name.toLowerCase().includes(searchTerm) || prompt.title.toLowerCase().includes(searchTerm) || prompt.description.toLowerCase().includes(searchTerm) || prompt.category.toLowerCase().includes(searchTerm), ); } static renderPrompt(name: string, args: Record): string { const prompt = this.getPrompt(name); if (!prompt) { throw new Error(`Prompt "${name}" not found`); } // Simple template rendering with Handlebars-like syntax let rendered = prompt.template; // Replace {{variable}} placeholders rendered = rendered.replace(/\{\{(\w+)\}\}/g, (match, varName) => { return args[varName] || ''; }); // Replace {{variable || default}} placeholders rendered = rendered.replace( /\{\{(\w+)\s*\|\|\s*'([^']*)'\}\}/g, (match, varName, defaultValue) => { return args[varName] || defaultValue; }, ); // Handle conditional blocks {{#if variable}}...{{/if}} rendered = rendered.replace( /\{\{#if\s+(\w+)\}\}([\s\S]*?)\{\{\/if\}\}/g, (match, varName, content) => { return args[varName] ? content : ''; }, ); return rendered.trim(); } } export function registerPromptsSystem(server: McpServer) { // Register all prompts using the SDK's prompt API const allPrompts = PromptsManager.getAllPrompts(); for (const promptTemplate of allPrompts) { // Convert arguments to proper Zod schema format const argsSchema = promptTemplate.arguments.reduce( (acc, arg) => { if (arg.required) { acc[arg.name] = z.string().describe(arg.description); } else { acc[arg.name] = z.string().optional().describe(arg.description); } return acc; }, {} as Record>, ); server.prompt( promptTemplate.name, promptTemplate.description, argsSchema, async (args: Record) => { const renderedPrompt = PromptsManager.renderPrompt( promptTemplate.name, args, ); return { messages: [ { role: 'user', content: { type: 'text', text: renderedPrompt, }, }, ], }; }, ); } }