Skip to main content
Glama
server-prompt.ts13.9 kB
/** * Prompt Shortcut MCP Server Implementation * * This implements the Model Context Protocol server for custom prompt shortcuts * allowing users to create and use shortcuts like /th, /ider, etc. */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, CallToolResult, TextContent } from '@modelcontextprotocol/sdk/types.js'; import { PromptShortcutManager } from './shortcuts/prompt-manager.js'; import { SecurityManager } from './security/manager.js'; import { Configuration } from './types/config.js'; import { Logger } from './utils/logger.js'; import { ShortcutCategory } from './types/prompt-shortcuts.js'; export class PromptShortcutMCPServer { private server: Server; private shortcutManager: PromptShortcutManager; private securityManager: SecurityManager; private config: Configuration; private logger: Logger; constructor(config: Configuration, logger: Logger) { this.config = config; this.logger = logger; // Initialize server this.server = new Server( { name: config.server.name, version: config.server.version, }, { capabilities: { tools: {}, }, } ); // Initialize managers this.shortcutManager = new PromptShortcutManager(logger); this.securityManager = new SecurityManager(config.security, logger); this.setupToolHandlers(); this.setupErrorHandlers(); } private setupToolHandlers(): void { // List available tools this.server.setRequestHandler(ListToolsRequestSchema, async () => { const tools: Tool[] = [ { name: 'parse_shortcut', description: 'Parse user input for shortcut commands and expand them', inputSchema: { type: 'object', properties: { input: { type: 'string', description: 'User input that may contain shortcut commands like /th or /ider' } }, required: ['input'] } }, { name: 'list_shortcuts', description: 'List all available prompt shortcuts with optional filtering', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Filter by category', enum: ['thinking', 'writing', 'coding', 'explaining', 'analysis', 'creative', 'productivity', 'personal'] }, search: { type: 'string', description: 'Search shortcuts by name, command, or description' }, favorites: { type: 'boolean', description: 'Show only favorite shortcuts' }, limit: { type: 'number', description: 'Maximum number of shortcuts to return' } } } }, { name: 'create_shortcut', description: 'Create a new custom prompt shortcut', inputSchema: { type: 'object', properties: { command: { type: 'string', description: 'Shortcut command (e.g., "/th", "/myshortcut")' }, name: { type: 'string', description: 'Display name for the shortcut' }, description: { type: 'string', description: 'Description of what this shortcut does' }, prompt: { type: 'string', description: 'The prompt template with {input} placeholder' }, category: { type: 'string', description: 'Category for organization', enum: ['thinking', 'writing', 'coding', 'explaining', 'analysis', 'creative', 'productivity', 'personal'] }, tags: { type: 'array', items: { type: 'string' }, description: 'Tags for categorization' } }, required: ['command', 'prompt'] } }, { name: 'update_shortcut', description: 'Update an existing shortcut', inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'ID of the shortcut to update' }, command: { type: 'string' }, name: { type: 'string' }, description: { type: 'string' }, prompt: { type: 'string' }, category: { type: 'string' }, tags: { type: 'array', items: { type: 'string' } }, isFavorite: { type: 'boolean' } }, required: ['id'] } }, { name: 'delete_shortcut', description: 'Delete a shortcut', inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'ID of the shortcut to delete' }, confirm: { type: 'boolean', description: 'Confirmation flag' } }, required: ['id', 'confirm'] } }, { name: 'get_shortcut_details', description: 'Get detailed information about a specific shortcut', inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'Shortcut ID' } }, required: ['id'] } } ]; return { tools }; }); // Handle tool calls this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { // Security validation const securityResult = this.securityManager.validateRequest(request); if (!securityResult.allowed) { throw new Error(`Security check failed: ${securityResult.reason}`); } // Route to appropriate handler switch (name) { case 'parse_shortcut': return await this.handleParseShortcut(args); case 'list_shortcuts': return await this.handleListShortcuts(args); case 'create_shortcut': return await this.handleCreateShortcut(args); case 'update_shortcut': return await this.handleUpdateShortcut(args); case 'delete_shortcut': return await this.handleDeleteShortcut(args); case 'get_shortcut_details': return await this.handleGetShortcutDetails(args); default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { this.logger.error('Tool execution failed:', { tool: name, args, error: error instanceof Error ? error.message : String(error) }); return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` } as TextContent ], isError: true }; } }); } private async handleParseShortcut(args: any): Promise<CallToolResult> { if (!args?.input) { throw new Error('Input is required'); } this.logger.debug('Parsing shortcut:', { input: args.input }); const parsed = this.shortcutManager.parseCommand(args.input); if (!parsed.isShortcut) { return { content: [ { type: 'text', text: `No shortcut detected in: "${args.input}"` } as TextContent ] }; } const execution = this.shortcutManager.executeShortcut(parsed); return { content: [ { type: 'text', text: `🔧 Shortcut Expanded: ${parsed.command}\n\n**Expanded Prompt:**\n${execution.expandedPrompt}` } as TextContent ] }; } private async handleListShortcuts(args: any): Promise<CallToolResult> { this.logger.debug('Listing shortcuts with args:', args); const shortcuts = this.shortcutManager.listShortcuts({ category: args?.category as ShortcutCategory, search: args?.search, favorites: args?.favorites, limit: args?.limit || 50 }); if (shortcuts.length === 0) { return { content: [ { type: 'text', text: 'No shortcuts found. Create your first shortcut with the create_shortcut tool!' } as TextContent ] }; } let response = `Found ${shortcuts.length} shortcut(s):\n\n`; shortcuts.forEach((shortcut, index) => { response += `${index + 1}. **${shortcut.command}** - ${shortcut.name}`; if (shortcut.isFavorite) response += ' ⭐'; response += `\n ${shortcut.description}\n`; response += ` Category: ${shortcut.category} | Uses: ${shortcut.usageCount}\n`; if (shortcut.tags.length > 0) { response += ` Tags: ${shortcut.tags.join(', ')}\n`; } response += `\n`; }); return { content: [ { type: 'text', text: response.trim() } as TextContent ] }; } private async handleCreateShortcut(args: any): Promise<CallToolResult> { if (!args?.command || !args?.prompt) { throw new Error('Command and prompt are required'); } this.logger.info('Creating shortcut:', { command: args.command }); const shortcut = this.shortcutManager.createShortcut({ command: args.command, name: args.name, description: args.description, prompt: args.prompt, category: args.category as ShortcutCategory, tags: args.tags || [], author: 'aezi zhu' }); return { content: [ { type: 'text', text: `✅ Created shortcut: **${shortcut.command}** - ${shortcut.name}\n\n**Prompt:** ${shortcut.prompt}\n\n**Usage:** Type "${shortcut.command} [your input]" to use this shortcut.` } as TextContent ] }; } private async handleUpdateShortcut(args: any): Promise<CallToolResult> { if (!args?.id) { throw new Error('Shortcut ID is required'); } this.logger.info('Updating shortcut:', { id: args.id }); const updated = this.shortcutManager.updateShortcut(args.id, args); return { content: [ { type: 'text', text: `✅ Updated shortcut: **${updated.command}** - ${updated.name}` } as TextContent ] }; } private async handleDeleteShortcut(args: any): Promise<CallToolResult> { if (!args?.id) { throw new Error('Shortcut ID is required'); } if (!args.confirm) { throw new Error('Confirmation required. Set confirm=true to delete.'); } this.logger.info('Deleting shortcut:', { id: args.id }); const success = this.shortcutManager.deleteShortcut(args.id); if (success) { return { content: [ { type: 'text', text: `✅ Shortcut deleted successfully` } as TextContent ] }; } else { throw new Error('Shortcut not found'); } } private async handleGetShortcutDetails(args: any): Promise<CallToolResult> { if (!args?.id) { throw new Error('Shortcut ID is required'); } const shortcut = this.shortcutManager.getShortcut(args.id); if (!shortcut) { throw new Error('Shortcut not found'); } let response = `**${shortcut.command}** - ${shortcut.name}\n\n`; response += `**Description:** ${shortcut.description}\n`; response += `**Category:** ${shortcut.category}\n`; response += `**Prompt Template:** ${shortcut.prompt}\n`; response += `**Usage Count:** ${shortcut.usageCount}\n`; response += `**Created:** ${shortcut.createdAt.toLocaleDateString()}\n`; response += `**Updated:** ${shortcut.updatedAt.toLocaleDateString()}\n`; if (shortcut.tags.length > 0) { response += `**Tags:** ${shortcut.tags.join(', ')}\n`; } if (shortcut.author) { response += `**Author:** ${shortcut.author}\n`; } return { content: [ { type: 'text', text: response } as TextContent ] }; } private setupErrorHandlers(): void { this.server.onerror = (error) => { this.logger.error('MCP Server error:', error); }; process.on('SIGINT', async () => { this.logger.info('Received SIGINT, shutting down...'); await this.stop(); process.exit(0); }); process.on('SIGTERM', async () => { this.logger.info('Received SIGTERM, shutting down...'); await this.stop(); process.exit(0); }); } async start(): Promise<void> { const transport = new StdioServerTransport(); this.logger.info('Starting Prompt Shortcut MCP Server...', { name: this.config.server.name, version: this.config.server.version }); // Initialize shortcut manager await this.shortcutManager.initialize(); await this.server.connect(transport); this.logger.info('Prompt Shortcut MCP Server connected and ready'); } async stop(): Promise<void> { this.logger.info('Stopping Prompt Shortcut MCP Server...'); await this.server.close(); this.logger.info('Prompt Shortcut MCP Server stopped'); } }

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/aezizhu/shortcut-mcp'

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