Files
myeasycms-v2/packages/mcp-server/src/tools/prompts.ts
Giancarlo Buomprisco 2b8572baaa Claude sub-agents, PRD, MCP improvements (#359)
1. Added Claude Code sub-agents
2. Added PRD tool to MCP Server
3. Added MCP Server UI to Dev Tools
4. Improved MCP Server Database Tool
5. Updated dependencies
2025-09-25 12:03:53 +08:00

308 lines
9.5 KiB
TypeScript

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, string>): 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<string, z.ZodString | z.ZodOptional<z.ZodString>>,
);
server.prompt(
promptTemplate.name,
promptTemplate.description,
argsSchema,
async (args: Record<string, string>) => {
const renderedPrompt = PromptsManager.renderPrompt(
promptTemplate.name,
args,
);
return {
messages: [
{
role: 'user',
content: {
type: 'text',
text: renderedPrompt,
},
},
],
};
},
);
}
}