Skip to main content
Glama

RISEN Prompt Engineering MCP Tool

server.js39.2 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; import sqlite3 from 'sqlite3'; import { promisify } from 'util'; import { v4 as uuidv4 } from 'uuid'; import fs from 'fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Log to stderr console.error('[RISEN] Starting RISEN Prompts MCP Server...'); // Database setup with error handling const dbPath = path.join(__dirname, 'risen_prompts.db'); const db = new sqlite3.Database(dbPath, (err) => { if (err) { console.error('[RISEN] Failed to connect to the database:', err.message); process.exit(1); } console.error('[RISEN] Database connected successfully'); }); // Promisify database methods const dbRun = promisify(db.run.bind(db)); const dbGet = promisify(db.get.bind(db)); const dbAll = promisify(db.all.bind(db)); // Initialize database with error handling async function initDatabase() { try { console.error('[RISEN] Initializing database...'); await dbRun(` CREATE TABLE IF NOT EXISTS templates ( id TEXT PRIMARY KEY, name TEXT NOT NULL, description TEXT, role TEXT, instructions TEXT, steps TEXT, expectations TEXT, narrowing TEXT, variables TEXT, tags TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, uses INTEGER DEFAULT 0, total_rating INTEGER DEFAULT 0, rating_count INTEGER DEFAULT 0 ) `); console.error('[RISEN] Templates table created/verified'); await dbRun(` CREATE TABLE IF NOT EXISTS experiments ( id TEXT PRIMARY KEY, template_id TEXT, executed_prompt TEXT, variables_used TEXT, ai_model TEXT, response TEXT, rating INTEGER, notes TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(template_id) REFERENCES templates(id) ) `); console.error('[RISEN] Experiments table created/verified'); // Create default templates await createDefaultTemplates(); console.error('[RISEN] Database initialization completed successfully'); } catch (error) { console.error('[RISEN] Database initialization failed:', error.message); console.error('[RISEN] Full error details:', error); throw new Error(`Database initialization failed: ${error.message}`); } } async function createDefaultTemplates() { try { const defaultTemplates = [ { name: "Code Review", description: "Comprehensive code review template", role: "Senior software engineer with 15+ years of experience in code quality and best practices", instructions: "Review the provided code for quality, performance, security, and maintainability", steps: JSON.stringify([ "Analyze code structure and organization", "Check for potential bugs and edge cases", "Evaluate performance implications", "Review security vulnerabilities", "Suggest improvements and best practices" ]), expectations: "Detailed review with specific line-by-line feedback and actionable suggestions", narrowing: "Focus on critical issues first, then style and minor improvements", variables: JSON.stringify(["code_snippet", "programming_language", "context"]), tags: JSON.stringify(["development", "code-review", "quality"]) }, { name: "Blog Post Writer", description: "SEO-optimized blog post creation", role: "Content strategist and SEO expert with proven track record in {{industry}}", instructions: "Write an engaging blog post about {{topic}} targeting {{audience}}", steps: JSON.stringify([ "Research keywords and current trends", "Create compelling headline and introduction", "Develop main points with examples", "Include relevant statistics and sources", "Write conclusion with call-to-action" ]), expectations: "1500-2000 word blog post, SEO-optimized, engaging tone, well-researched", narrowing: "Use conversational tone, include 3-5 keywords naturally, target readability score of 60+", variables: JSON.stringify(["topic", "audience", "industry", "keywords"]), tags: JSON.stringify(["content", "blog", "seo", "writing"]) }, { name: "Data Analysis", description: "Comprehensive data analysis and insights", role: "Data scientist specializing in {{domain}} with expertise in statistical analysis", instructions: "Analyze the {{dataset_description}} to uncover insights and patterns", steps: JSON.stringify([ "Perform exploratory data analysis", "Identify key trends and patterns", "Run statistical significance tests", "Create visualizations for findings", "Provide actionable recommendations" ]), expectations: "Clear insights with statistical backing, visualization suggestions, business recommendations", narrowing: "Focus on {{specific_metrics}} and their business impact", variables: JSON.stringify(["domain", "dataset_description", "specific_metrics"]), tags: JSON.stringify(["data", "analysis", "insights", "statistics"]) } ]; console.error(`[RISEN] Creating ${defaultTemplates.length} default templates...`); let created = 0; let skipped = 0; for (const template of defaultTemplates) { try { const existing = await dbGet('SELECT id FROM templates WHERE name = ?', template.name); if (!existing) { await dbRun( `INSERT INTO templates (id, name, description, role, instructions, steps, expectations, narrowing, variables, tags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, uuidv4(), template.name, template.description, template.role, template.instructions, template.steps, template.expectations, template.narrowing, template.variables, template.tags ); created++; console.error(`[RISEN] Created default template: ${template.name}`); } else { skipped++; console.error(`[RISEN] Skipped existing template: ${template.name}`); } } catch (templateError) { console.error(`[RISEN] Failed to create template "${template.name}":`, templateError.message); // Continue with other templates instead of failing completely } } console.error(`[RISEN] Default templates processed: ${created} created, ${skipped} skipped`); } catch (error) { console.error('[RISEN] Error in createDefaultTemplates:', error.message); throw new Error(`Failed to create default templates: ${error.message}`); } } // RISEN Validation function validateRISEN(template) { const errors = []; const warnings = []; // Check required components if (!template.role || template.role.trim().length < 10) { errors.push("Role must be at least 10 characters and clearly define the AI's persona"); } if (!template.instructions || template.instructions.trim().length < 20) { errors.push("Instructions must be at least 20 characters and provide clear directives"); } // Check steps let steps = []; try { steps = JSON.parse(template.steps || '[]'); } catch (e) { errors.push("Steps must be a valid JSON array"); } if (steps.length < 2) { warnings.push("Consider adding more steps for better task breakdown"); } if (!template.expectations || template.expectations.trim().length < 15) { errors.push("Expectations must clearly define the desired outcome (min 15 chars)"); } if (!template.narrowing || template.narrowing.trim().length < 10) { warnings.push("Narrowing/Novelty helps focus or expand the task - consider adding constraints or creative elements"); } // Check for variable usage const variableRegex = /\{\{(\w+)\}\}/g; const usedVars = new Set(); [template.role, template.instructions, template.expectations, template.narrowing].forEach(text => { if (text) { const matches = text.matchAll(variableRegex); for (const match of matches) { usedVars.add(match[1]); } } }); // Check if steps use variables steps.forEach(step => { const matches = step.matchAll(variableRegex); for (const match of matches) { usedVars.add(match[1]); } }); // Validate declared variables match used variables let declaredVars = []; try { declaredVars = JSON.parse(template.variables || '[]'); } catch (e) { if (usedVars.size > 0) { errors.push("Variables must be a valid JSON array"); } } const declaredSet = new Set(declaredVars); usedVars.forEach(v => { if (!declaredSet.has(v)) { warnings.push(`Variable {{${v}}} is used but not declared`); } }); declaredSet.forEach(v => { if (!usedVars.has(v)) { warnings.push(`Variable {{${v}}} is declared but not used`); } }); return { valid: errors.length === 0, errors, warnings, score: calculateQualityScore(template) }; } function calculateQualityScore(template) { let score = 0; // Role quality (20 points) if (template.role && template.role.length > 30) score += 10; if (template.role && template.role.length > 50) score += 10; // Instructions clarity (20 points) if (template.instructions && template.instructions.length > 50) score += 10; if (template.instructions && template.instructions.includes('{{')) score += 10; // Uses variables // Steps detail (20 points) try { const steps = JSON.parse(template.steps || '[]'); if (steps.length >= 3) score += 10; if (steps.length >= 5) score += 10; } catch (e) {} // Expectations specificity (20 points) if (template.expectations && template.expectations.length > 40) score += 10; if (template.expectations && /\d+/.test(template.expectations)) score += 10; // Contains numbers/metrics // Narrowing focus (20 points) if (template.narrowing && template.narrowing.length > 30) score += 10; if (template.narrowing && (template.narrowing.includes('avoid') || template.narrowing.includes('focus'))) score += 10; return score; } // Enhanced prompt suggestions using AI analysis function generateSuggestions(template) { const suggestions = []; // Role suggestions if (template.role && template.role.length < 30) { suggestions.push({ component: "role", suggestion: "Consider adding more specific expertise, years of experience, or domain knowledge to the role" }); } // Instructions suggestions if (template.instructions && !template.instructions.includes('{{')) { suggestions.push({ component: "instructions", suggestion: "Consider using variables (e.g., {{topic}}) to make this template reusable" }); } // Steps suggestions try { const steps = JSON.parse(template.steps || '[]'); if (steps.length < 3) { suggestions.push({ component: "steps", suggestion: "Break down the task into more detailed steps for better AI guidance" }); } if (steps.some(step => step.length < 20)) { suggestions.push({ component: "steps", suggestion: "Some steps are too brief - add more detail for clarity" }); } } catch (e) {} // Expectations suggestions if (template.expectations && !(/\d+/.test(template.expectations))) { suggestions.push({ component: "expectations", suggestion: "Consider adding specific metrics or measurable outcomes (e.g., word count, number of examples)" }); } // Narrowing suggestions if (!template.narrowing || template.narrowing.length < 20) { suggestions.push({ component: "narrowing", suggestion: "Add constraints to focus the output or creative elements to encourage innovation" }); } return suggestions; } // Replace variables in template function applyVariables(template, variables) { let result = { role: template.role, instructions: template.instructions, steps: template.steps, expectations: template.expectations, narrowing: template.narrowing }; // Replace variables in all fields Object.keys(variables).forEach(key => { const value = variables[key]; const regex = new RegExp(`\\{\\{${key}\\}\\}`, 'g'); result.role = result.role.replace(regex, value); result.instructions = result.instructions.replace(regex, value); result.expectations = result.expectations.replace(regex, value); result.narrowing = result.narrowing.replace(regex, value); // Handle steps array try { const steps = JSON.parse(result.steps); const updatedSteps = steps.map(step => step.replace(regex, value)); result.steps = JSON.stringify(updatedSteps); } catch (e) {} }); return result; } // Format RISEN prompt for execution function formatRISENPrompt(template) { const steps = JSON.parse(template.steps || '[]'); return `Role: ${template.role} Instructions: ${template.instructions} Steps: ${steps.map((step, i) => `${i + 1}. ${step}`).join('\n')} Expectations: ${template.expectations} Narrowing: ${template.narrowing}`; } // MCP Server Setup const server = new Server( { name: 'mcp-risen-prompts', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // Tool handlers server.setRequestHandler(ListToolsRequestSchema, async () => { console.error('[RISEN] Handling tools/list request'); return { tools: [ { name: 'risen_create', description: 'Create a new RISEN prompt template', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Template name' }, description: { type: 'string', description: 'Template description' }, role: { type: 'string', description: 'AI role/persona' }, instructions: { type: 'string', description: 'Clear directives' }, steps: { type: 'array', items: { type: 'string' }, description: 'Task breakdown' }, expectations: { type: 'string', description: 'Desired outcome' }, narrowing: { type: 'string', description: 'Constraints or creative elements' }, variables: { type: 'array', items: { type: 'string' }, description: 'Template variables' }, tags: { type: 'array', items: { type: 'string' }, description: 'Tags for organization' } }, required: ['name', 'role', 'instructions', 'steps', 'expectations'] } }, { name: 'risen_validate', description: 'Validate a RISEN prompt structure and get improvement suggestions', inputSchema: { type: 'object', properties: { template_id: { type: 'string', description: 'Template ID to validate' }, template: { type: 'object', description: 'Or provide template directly', properties: { role: { type: 'string' }, instructions: { type: 'string' }, steps: { type: 'array', items: { type: 'string' } }, expectations: { type: 'string' }, narrowing: { type: 'string' } } } } } }, { name: 'risen_execute', description: 'Execute a RISEN prompt template with variables', inputSchema: { type: 'object', properties: { template_id: { type: 'string', description: 'Template ID' }, variables: { type: 'object', description: 'Variables to fill in template' } }, required: ['template_id'] } }, { name: 'risen_track', description: 'Track the results of a RISEN prompt execution', inputSchema: { type: 'object', properties: { template_id: { type: 'string', description: 'Template ID' }, executed_prompt: { type: 'string', description: 'The executed prompt' }, variables_used: { type: 'object', description: 'Variables that were used' }, ai_model: { type: 'string', description: 'Which AI model was used' }, response: { type: 'string', description: 'AI response (truncated if needed)' }, rating: { type: 'integer', minimum: 1, maximum: 5, description: 'Rating 1-5' }, notes: { type: 'string', description: 'Additional notes' } }, required: ['template_id', 'rating'] } }, { name: 'risen_search', description: 'Search for RISEN templates with pagination support', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query' }, tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags' }, min_rating: { type: 'number', description: 'Minimum average rating' }, offset: { type: 'integer', minimum: 0, default: 0, description: 'Pagination offset (starting record)' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 20, description: 'Number of results per page (max 100)' } } } }, { name: 'risen_analyze', description: 'Analyze template performance and get insights with pagination', inputSchema: { type: 'object', properties: { template_id: { type: 'string', description: 'Template ID to analyze' }, offset: { type: 'integer', minimum: 0, default: 0, description: 'Pagination offset for experiments' }, limit: { type: 'integer', minimum: 1, maximum: 50, default: 10, description: 'Number of experiments per page (max 50)' } }, required: ['template_id'] } }, { name: 'risen_suggest', description: 'Get AI-powered suggestions to improve a RISEN prompt', inputSchema: { type: 'object', properties: { template_id: { type: 'string', description: 'Template ID' } }, required: ['template_id'] } }, { name: 'risen_convert', description: 'Convert a natural language request into RISEN format', inputSchema: { type: 'object', properties: { request: { type: 'string', description: 'Natural language request' }, context: { type: 'string', description: 'Additional context' } }, required: ['request'] } } ], }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { console.error('[RISEN] Handling tools/call request:', request.params.name); const { name, arguments: args } = request.params; try { switch (name) { case 'risen_create': { const validation = validateRISEN({ role: args.role, instructions: args.instructions, steps: JSON.stringify(args.steps), expectations: args.expectations, narrowing: args.narrowing || '', variables: JSON.stringify(args.variables || []) }); if (!validation.valid) { return { content: [{ type: 'text', text: `❌ Validation failed:\n${validation.errors.join('\n')}\n\n⚠️ Warnings:\n${validation.warnings.join('\n')}` }] }; } const id = uuidv4(); await dbRun( `INSERT INTO templates (id, name, description, role, instructions, steps, expectations, narrowing, variables, tags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, id, args.name, args.description || '', args.role, args.instructions, JSON.stringify(args.steps), args.expectations, args.narrowing || '', JSON.stringify(args.variables || []), JSON.stringify(args.tags || []) ); return { content: [{ type: 'text', text: `✅ RISEN template created successfully! ID: ${id} Name: ${args.name} Quality Score: ${validation.score}/100 ${validation.warnings.length > 0 ? '\n⚠️ Suggestions:\n' + validation.warnings.join('\n') : ''}` }] }; } case 'risen_validate': { let template; if (args.template_id) { template = await dbGet('SELECT * FROM templates WHERE id = ?', args.template_id); if (!template) { return { content: [{ type: 'text', text: '❌ Template not found' }] }; } } else if (args.template) { template = { role: args.template.role, instructions: args.template.instructions, steps: JSON.stringify(args.template.steps || []), expectations: args.template.expectations, narrowing: args.template.narrowing || '' }; } else { return { content: [{ type: 'text', text: '❌ Provide either template_id or template object' }] }; } const validation = validateRISEN(template); const suggestions = generateSuggestions(template); return { content: [{ type: 'text', text: `🔍 RISEN Validation Report ✅ Valid: ${validation.valid ? 'Yes' : 'No'} 📊 Quality Score: ${validation.score}/100 ${validation.errors.length > 0 ? '❌ Errors:\n' + validation.errors.map(e => `• ${e}`).join('\n') : ''} ${validation.warnings.length > 0 ? '⚠️ Warnings:\n' + validation.warnings.map(w => `• ${w}`).join('\n') : ''} 💡 Improvement Suggestions: ${suggestions.map(s => `• [${s.component.toUpperCase()}] ${s.suggestion}`).join('\n')}` }] }; } case 'risen_execute': { const template = await dbGet('SELECT * FROM templates WHERE id = ?', args.template_id); if (!template) { return { content: [{ type: 'text', text: '❌ Template not found' }] }; } // Apply variables if provided let finalTemplate = template; if (args.variables) { finalTemplate = applyVariables(template, args.variables); } // Check for missing variables const variableRegex = /\{\{(\w+)\}\}/g; const remainingVars = []; const allText = `${finalTemplate.role} ${finalTemplate.instructions} ${finalTemplate.expectations} ${finalTemplate.narrowing} ${finalTemplate.steps}`; const matches = allText.matchAll(variableRegex); for (const match of matches) { remainingVars.push(match[1]); } if (remainingVars.length > 0) { return { content: [{ type: 'text', text: `⚠️ Missing variables: ${[...new Set(remainingVars)].join(', ')}\n\nPlease provide values for all variables.` }] }; } // Format the prompt const prompt = formatRISENPrompt(finalTemplate); // Update usage count await dbRun('UPDATE templates SET uses = uses + 1 WHERE id = ?', args.template_id); return { content: [{ type: 'text', text: `📝 RISEN Prompt Ready:\n\n${prompt}\n\n✅ Template "${template.name}" executed (${template.uses + 1} total uses)` }] }; } case 'risen_track': { const id = uuidv4(); const template = await dbGet('SELECT * FROM templates WHERE id = ?', args.template_id); if (!template) { return { content: [{ type: 'text', text: '❌ Template not found' }] }; } // Store experiment await dbRun( `INSERT INTO experiments (id, template_id, executed_prompt, variables_used, ai_model, response, rating, notes) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, id, args.template_id, args.executed_prompt || '', JSON.stringify(args.variables_used || {}), args.ai_model || 'claude', args.response ? args.response.substring(0, 1000) : '', // Truncate long responses args.rating, args.notes || '' ); // Update template rating await dbRun( `UPDATE templates SET total_rating = total_rating + ?, rating_count = rating_count + 1 WHERE id = ?`, args.rating, args.template_id ); const updatedTemplate = await dbGet('SELECT * FROM templates WHERE id = ?', args.template_id); const avgRating = updatedTemplate.total_rating / updatedTemplate.rating_count; return { content: [{ type: 'text', text: `📊 Experiment tracked! Template: ${template.name} Rating: ${'⭐'.repeat(args.rating)} Average Rating: ${avgRating.toFixed(1)} (${updatedTemplate.rating_count} ratings) AI Model: ${args.ai_model || 'claude'} ${args.notes ? `Notes: ${args.notes}` : ''}` }] }; } case 'risen_search': { // Pagination parameters with validation const offset = Math.max(0, parseInt(args.offset) || 0); const limit = Math.min(100, Math.max(1, parseInt(args.limit) || 20)); // Build the main query let query = 'SELECT * FROM templates WHERE 1=1'; const params = []; if (args.query) { query += ' AND (name LIKE ? OR description LIKE ? OR tags LIKE ?)'; const searchTerm = `%${args.query}%`; params.push(searchTerm, searchTerm, searchTerm); } if (args.tags && args.tags.length > 0) { // Secure parameterized query for tags - prevent SQL injection const placeholders = args.tags.map(() => 'tags LIKE ?').join(' OR '); query += ` AND (${placeholders})`; args.tags.forEach(tag => params.push(`%"${tag}"%`)); } if (args.min_rating) { query += ' AND (total_rating / NULLIF(rating_count, 0)) >= ?'; params.push(args.min_rating); } // Build count query for pagination metadata const countQuery = query.replace('SELECT * FROM', 'SELECT COUNT(*) as total FROM'); const countResult = await dbGet(countQuery, ...params); const totalCount = countResult ? countResult.total : 0; // Add pagination to main query query += ' ORDER BY uses DESC, rating_count DESC LIMIT ? OFFSET ?'; params.push(limit, offset); const templates = await dbAll(query, ...params); if (templates.length === 0 && totalCount === 0) { return { content: [{ type: 'text', text: '❌ No templates found matching your criteria' }] }; } // Format results with pagination metadata const results = templates.map(t => { const avgRating = t.rating_count > 0 ? (t.total_rating / t.rating_count).toFixed(1) : 'N/A'; const tags = JSON.parse(t.tags || '[]').join(', '); return `📝 ${t.name} (ID: ${t.id}) ${t.description} ⭐ Rating: ${avgRating} | 🔧 Uses: ${t.uses} | 🏷️ Tags: ${tags}`; }).join('\n\n'); // Calculate pagination info const hasMore = offset + limit < totalCount; const currentPage = Math.floor(offset / limit) + 1; const totalPages = Math.ceil(totalCount / limit); return { content: [{ type: 'text', text: `🔍 Found ${totalCount} templates (showing ${templates.length}): ${results} 📄 Page ${currentPage} of ${totalPages} | Showing ${offset + 1}-${offset + templates.length} of ${totalCount} results${hasMore ? `\n\n➡️ Use offset: ${offset + limit} for next page` : ''}` }] }; } case 'risen_analyze': { const template = await dbGet('SELECT * FROM templates WHERE id = ?', args.template_id); if (!template) { return { content: [{ type: 'text', text: '❌ Template not found' }] }; } // Pagination parameters for experiments const offset = Math.max(0, parseInt(args.offset) || 0); const limit = Math.min(50, Math.max(1, parseInt(args.limit) || 10)); // Get total count of experiments const experimentCountResult = await dbGet( 'SELECT COUNT(*) as total FROM experiments WHERE template_id = ?', args.template_id ); const totalExperiments = experimentCountResult ? experimentCountResult.total : 0; // Get paginated experiments const experiments = await dbAll( 'SELECT * FROM experiments WHERE template_id = ? ORDER BY created_at DESC LIMIT ? OFFSET ?', args.template_id, limit, offset ); const avgRating = template.rating_count > 0 ? (template.total_rating / template.rating_count).toFixed(1) : 'N/A'; // Analyze performance by AI model const modelStats = {}; experiments.forEach(exp => { const model = exp.ai_model || 'claude'; if (!modelStats[model]) { modelStats[model] = { count: 0, totalRating: 0 }; } modelStats[model].count++; modelStats[model].totalRating += exp.rating; }); const modelAnalysis = Object.entries(modelStats).map(([model, stats]) => { const avg = (stats.totalRating / stats.count).toFixed(1); return `${model}: ${avg} avg (${stats.count} uses)`; }).join('\n '); // Get common feedback themes from current page const notes = experiments.filter(e => e.notes).map(e => e.notes); // Pagination info for experiments const hasMoreExperiments = offset + limit < totalExperiments; const currentPage = Math.floor(offset / limit) + 1; const totalPages = Math.ceil(totalExperiments / limit); return { content: [{ type: 'text', text: `📊 Template Analysis: ${template.name} 📈 Overall Performance: Total Uses: ${template.uses} Average Rating: ${avgRating} ⭐ Total Ratings: ${template.rating_count} Total Experiments: ${totalExperiments} 🤖 Performance by AI Model: ${modelAnalysis || 'No model-specific data yet'} 💬 Recent Feedback (Page ${currentPage} of ${totalPages}): ${notes.length > 0 ? notes.slice(0, 3).map(n => ` • ${n}`).join('\n') : ' No feedback notes yet'} 🎯 Quality Score: ${calculateQualityScore(template)}/100 💡 Recommendations: ${avgRating < 3 ? ' • Consider refining the prompt structure\n' : ''}${template.uses < 5 ? ' • Need more usage data for reliable insights\n' : ''}${notes.length < 3 ? ' • Encourage users to leave feedback notes\n' : ''}${hasMoreExperiments ? `\n➡️ Use offset: ${offset + limit} to see more experiments` : ''}` }] }; } case 'risen_suggest': { const template = await dbGet('SELECT * FROM templates WHERE id = ?', args.template_id); if (!template) { return { content: [{ type: 'text', text: '❌ Template not found' }] }; } const validation = validateRISEN(template); const suggestions = generateSuggestions(template); // Get performance data const avgRating = template.rating_count > 0 ? (template.total_rating / template.rating_count).toFixed(1) : null; // Generate enhanced suggestions based on performance const enhancedSuggestions = []; if (avgRating && parseFloat(avgRating) < 3) { enhancedSuggestions.push("Low ratings suggest the prompt may be too vague or complex. Consider simplifying."); } if (template.uses > 10 && (!avgRating || parseFloat(avgRating) < 4)) { enhancedSuggestions.push("With many uses but moderate ratings, gather user feedback to identify specific issues."); } // Component-specific enhanced suggestions const roleWords = template.role.split(' ').length; if (roleWords < 8) { enhancedSuggestions.push("Role is very brief. Try: 'Expert [domain] professional with [X] years experience in [specific areas], specialized in [unique skills]'"); } try { const steps = JSON.parse(template.steps); if (steps.some(s => s.split(' ').length < 5)) { enhancedSuggestions.push("Some steps are too brief. Each step should be a complete, actionable instruction."); } } catch (e) {} return { content: [{ type: 'text', text: `🎯 AI-Powered Suggestions for "${template.name}" 📊 Current Performance: Quality Score: ${validation.score}/100 Average Rating: ${avgRating || 'N/A'} ⭐ Total Uses: ${template.uses} 💡 Component Improvements: ${suggestions.map(s => `• [${s.component.toUpperCase()}] ${s.suggestion}`).join('\n')} 🚀 Enhanced Suggestions: ${enhancedSuggestions.map(s => `• ${s}`).join('\n')} 📝 Example Improvements: BEFORE: "${template.role.substring(0, 50)}..." AFTER: "Senior ${template.role.includes('expert') ? '' : 'expert '}${template.role} with deep knowledge of industry best practices and proven track record" 🎨 Pro Tips: • Use specific numbers in expectations (e.g., "5-7 actionable insights") • Include examples in narrowing (e.g., "Avoid jargon, write at 8th grade level") • Test with different variable combinations to find what works best` }] }; } case 'risen_convert': { // Simple conversion logic - in real implementation, this could use AI const { request, context } = args; // Extract potential components from the request const keywords = { role: ['as a', 'act as', 'you are', 'expert', 'specialist'], instructions: ['create', 'write', 'analyze', 'generate', 'help me'], expectations: ['should be', 'must include', 'make sure', 'i want', 'need'], narrowing: ['focus on', 'avoid', 'don\'t', 'specifically', 'only'] }; // Simple extraction logic let role = "Expert assistant"; let instructions = request; let expectations = "Clear, comprehensive, and actionable output"; let narrowing = "Focus on practical solutions"; // Try to extract role for (const keyword of keywords.role) { if (request.toLowerCase().includes(keyword)) { const startIdx = request.toLowerCase().indexOf(keyword); const endIdx = request.indexOf('.', startIdx); if (endIdx > startIdx) { role = request.substring(startIdx, endIdx).replace(keyword, '').trim(); break; } } } // Generate steps based on request type const steps = []; if (request.includes('analyze')) { steps.push('Examine the provided information thoroughly'); steps.push('Identify key patterns and insights'); steps.push('Draw conclusions based on analysis'); } else if (request.includes('write') || request.includes('create')) { steps.push('Plan the structure and key points'); steps.push('Develop the main content'); steps.push('Review and refine for clarity'); } else { steps.push('Understand the requirement'); steps.push('Process the information'); steps.push('Provide comprehensive response'); } // Extract any specific requirements if (context) { expectations += `. ${context}`; } const convertedTemplate = { role: role.charAt(0).toUpperCase() + role.slice(1), instructions: instructions, steps: steps, expectations: expectations, narrowing: narrowing }; return { content: [{ type: 'text', text: `🔄 Converted to RISEN format: **Role**: ${convertedTemplate.role} **Instructions**: ${convertedTemplate.instructions} **Steps**: ${convertedTemplate.steps.map((s, i) => `${i + 1}. ${s}`).join('\n')} **Expectations**: ${convertedTemplate.expectations} **Narrowing**: ${convertedTemplate.narrowing} 💡 Tips for improvement: • Refine the role to be more specific to your domain • Add more detailed steps based on your workflow • Include measurable expectations (e.g., word count, format) • Adjust narrowing to focus on your priorities Would you like to save this as a template?` }] }; } default: return { content: [{ type: 'text', text: `Unknown tool: ${name}` }] }; } } catch (error) { console.error('[RISEN] Tool handler error:', error); // Log full error for debugging return { content: [{ type: 'text', text: 'An unexpected error occurred. Please try again later.' }] }; } }); // Start server with retry logic async function main() { const maxRetries = 3; let retries = 0; while (retries < maxRetries) { try { await initDatabase(); break; // Exit loop if successful } catch (error) { console.error(`[RISEN] Database initialization failed (attempt ${retries + 1}/${maxRetries}):`, error.message); retries++; if (retries < maxRetries) { console.error(`[RISEN] Retrying in 2 seconds...`); await new Promise(resolve => setTimeout(resolve, 2000)); // Wait before retrying } } } if (retries === maxRetries) { console.error('[RISEN] Database initialization failed after multiple retries. Exiting.'); process.exit(1); // Exit with an error code } const transport = new StdioServerTransport(); await server.connect(transport); console.error('[RISEN] RISEN Prompts MCP Server is running!'); console.error(`[RISEN] Database path: ${dbPath}`); } main().catch((error) => { console.error('[RISEN] Fatal error:', error); process.exit(1); }); // Handle graceful shutdown process.on('SIGINT', () => { console.error('[RISEN] Shutting down...'); try { db.close((err) => { if (err) { console.error('[RISEN] Error closing database:', err.message); } else { console.error('[RISEN] Database closed successfully'); } process.exit(0); }); } catch (error) { console.error('[RISEN] Error during shutdown:', error.message); process.exit(1); } });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Futuretechaiguy/risen-prompts-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server