Skip to main content
Glama
mcpPrompts.ts7.93 kB
// MCP Prompts implementation for interactive templates import { Prompt, PromptMessage } from '@modelcontextprotocol/sdk/types.js'; import Handlebars from 'handlebars'; import { EnhancedPromptCache } from './enhancedCache.js'; import { MCPPrompt } from './types.js'; export class PromptTemplates { private cache: EnhancedPromptCache; private handlebars: typeof Handlebars; constructor(cache: EnhancedPromptCache) { this.cache = cache; this.handlebars = Handlebars.create(); this.registerHelpers(); } /** * Register Handlebars helpers */ private registerHelpers(): void { // Helper to join arrays this.handlebars.registerHelper('join', (array: any[], separator: string) => { return Array.isArray(array) ? array.join(separator) : ''; }); // Helper for conditional rendering this.handlebars.registerHelper('eq', (a: any, b: any) => a === b); // Helper to lowercase text this.handlebars.registerHelper('lowercase', (text: string) => { return text ? text.toLowerCase() : ''; }); // Helper to uppercase text this.handlebars.registerHelper('uppercase', (text: string) => { return text ? text.toUpperCase() : ''; }); } /** * List all available prompt templates */ async listPrompts(): Promise<Prompt[]> { const prompts = this.cache.getAllPrompts(); const mcpPrompts: Prompt[] = []; // Convert repository prompts to MCP prompts for (const prompt of prompts) { const mcpPrompt: Prompt = { name: prompt.name, description: prompt.metadata.description || `Use the ${prompt.name} prompt template`, }; // Add arguments if defined in metadata if (prompt.metadata.arguments && prompt.metadata.arguments.length > 0) { mcpPrompt.arguments = prompt.metadata.arguments.map(arg => ({ name: arg.name, description: arg.description, required: arg.required || false, })); } mcpPrompts.push(mcpPrompt); } // Add built-in interactive prompts mcpPrompts.push( { name: 'compose_prompt', description: 'Compose multiple prompts together', arguments: [ { name: 'prompts', description: 'Comma-separated list of prompt names to compose', required: true, }, { name: 'separator', description: 'Text to separate prompts (default: newline)', required: false, }, ], }, { name: 'search_prompts', description: 'Search for prompts by keyword', arguments: [ { name: 'query', description: 'Search query', required: true, }, { name: 'limit', description: 'Maximum number of results (default: 5)', required: false, }, ], }, { name: 'suggest_prompts', description: 'Get AI-powered prompt suggestions', arguments: [ { name: 'context', description: 'Describe what you want to accomplish', required: true, }, { name: 'count', description: 'Number of suggestions (default: 3)', required: false, }, ], } ); return mcpPrompts; } /** * Get a specific prompt with filled template */ async getPrompt(name: string, args?: Record<string, string>): Promise<PromptMessage[]> { // Handle built-in prompts switch (name) { case 'compose_prompt': return this.handleComposePrompt(args); case 'search_prompts': return this.handleSearchPrompts(args); case 'suggest_prompts': return this.handleSuggestPrompts(args); } // Handle repository prompts const prompt = this.cache.getPrompt(name); if (!prompt) { throw new Error(`Prompt not found: ${name}`); } // Prepare template context const context = { ...args, prompt_name: prompt.name, prompt_category: prompt.metadata.category, prompt_tags: prompt.metadata.tags, }; // Compile and render template let content = prompt.content || ''; try { const template = this.handlebars.compile(content); content = template(context); } catch (error) { console.error('Error rendering template:', error); } return [ { role: 'user', content: { type: 'text', text: content, }, }, ]; } /** * Handle compose_prompt built-in prompt */ private async handleComposePrompt(args?: Record<string, string>): Promise<PromptMessage[]> { if (!args?.prompts) { throw new Error('prompts argument is required'); } const promptNames = args.prompts.split(',').map(n => n.trim()); const separator = args.separator || '\n\n---\n\n'; const contents: string[] = []; for (const promptName of promptNames) { const prompt = this.cache.getPrompt(promptName); if (prompt && prompt.content) { contents.push(`# ${prompt.metadata.title || prompt.name}\n\n${prompt.content}`); } } return [ { role: 'user', content: { type: 'text', text: contents.join(separator), }, }, ]; } /** * Handle search_prompts built-in prompt */ private async handleSearchPrompts(args?: Record<string, string>): Promise<PromptMessage[]> { if (!args?.query) { throw new Error('query argument is required'); } const limit = parseInt(args.limit || '5', 10); const results = this.cache.searchPrompts(args.query).slice(0, limit); const content = `# Search Results for "${args.query}" Found ${results.length} prompts: ${results.map((prompt, i) => ` ${i + 1}. **${prompt.metadata.title || prompt.name}** - Category: ${prompt.metadata.category || 'general'} - Tags: ${prompt.metadata.tags?.join(', ') || 'none'} - Description: ${prompt.metadata.description || 'No description'} `).join('\n')} To use a prompt, ask for it by name.`; return [ { role: 'assistant', content: { type: 'text', text: content, }, }, ]; } /** * Handle suggest_prompts built-in prompt */ private async handleSuggestPrompts(args?: Record<string, string>): Promise<PromptMessage[]> { if (!args?.context) { throw new Error('context argument is required'); } const count = parseInt(args.count || '3', 10); // Simple keyword-based suggestion (could be enhanced with embeddings) const keywords = args.context.toLowerCase().split(/\s+/); const scoredPrompts = this.cache.getAllPrompts().map(prompt => { let score = 0; const searchText = `${prompt.metadata.title} ${prompt.metadata.description} ${prompt.metadata.tags?.join(' ')} ${prompt.content}`.toLowerCase(); for (const keyword of keywords) { if (searchText.includes(keyword)) { score += 1; } } return { prompt, score }; }); const suggestions = scoredPrompts .filter(s => s.score > 0) .sort((a, b) => b.score - a.score) .slice(0, count) .map(s => s.prompt); const content = `# Suggested Prompts for: "${args.context}" Based on your context, here are ${suggestions.length} relevant prompts: ${suggestions.map((prompt, i) => ` ${i + 1}. **${prompt.metadata.title || prompt.name}** - Category: ${prompt.metadata.category || 'general'} - Description: ${prompt.metadata.description || 'No description'} - Relevance: High `).join('\n')} To use any of these prompts, just ask for it by name.`; return [ { role: 'assistant', content: { type: 'text', text: content, }, }, ]; } }

Implementation Reference

Latest Blog Posts

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/jezweb/smart-prompts-mcp'

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