@rhofkens/mcp-quotes-server-claude-code
Version:
Model Context Protocol (MCP) server for managing and serving quotes
314 lines • 11.1 kB
JavaScript
/**
* MCP Quotes Server - Template Manager
*
* Central manager for all quote templates
*/
import { TemplateCategory } from '../../types/templates.js';
import { logger } from '../../utils/logger.js';
import { motivationalTemplates } from './categories/motivationalTemplates.js';
import { philosophicalTemplates } from './categories/philosophicalTemplates.js';
import { TemplateRenderer } from './generators/templateRenderer.js';
import { templateRepository } from './templateRepository.js';
import { TemplateValidator } from './validators/templateValidator.js';
/**
* Template Manager class - singleton pattern
*/
export class TemplateManager {
static instance;
initialized = false;
constructor() { }
/**
* Get singleton instance
*/
static getInstance() {
if (!TemplateManager.instance) {
TemplateManager.instance = new TemplateManager();
}
return TemplateManager.instance;
}
/**
* Initialize template manager
*/
async initialize() {
if (this.initialized) {
return;
}
try {
// Load all category templates
await this.loadCategoryTemplates();
this.initialized = true;
logger.info('Template Manager initialized successfully');
}
catch (error) {
logger.error('Failed to initialize Template Manager', error);
throw error;
}
}
/**
* Load all category templates into repository
*/
async loadCategoryTemplates() {
const allTemplates = [
...motivationalTemplates,
...philosophicalTemplates,
// Add more category templates as they are created
];
let successCount = 0;
let failureCount = 0;
for (const template of allTemplates) {
try {
await templateRepository.saveTemplate(template);
successCount++;
}
catch (error) {
logger.error('Failed to load template', {
templateId: template.metadata.id,
error: error instanceof Error ? error.message : String(error),
});
failureCount++;
}
}
logger.info('Category templates loaded', {
success: successCount,
failed: failureCount,
total: allTemplates.length,
});
}
/**
* Get a template by ID
*/
async getTemplate(id, version) {
return templateRepository.getTemplate(id, version);
}
/**
* List all templates in a category
*/
async listByCategory(category) {
return templateRepository.listByCategory(category);
}
/**
* Search templates
*/
async searchTemplates(query) {
return templateRepository.searchTemplates(query);
}
/**
* Validate a template
*/
validateTemplate(template) {
return TemplateValidator.validate(template);
}
/**
* Render a template
*/
async renderTemplate(templateId, context) {
const template = await this.getTemplate(templateId);
if (!template) {
throw new Error(`Template not found: ${templateId}`);
}
return TemplateRenderer.render(template, context);
}
/**
* Save a custom template
*/
async saveTemplate(template) {
// Validate before saving
const validation = this.validateTemplate(template);
if (!validation.isValid) {
throw new Error(`Template validation failed: ${validation.errors.map((e) => e.message).join(', ')}`);
}
await templateRepository.saveTemplate(template);
}
/**
* Get template suggestions based on context
*/
async getTemplateSuggestions(context) {
const query = {
categories: context.category ? [context.category] : [],
tags: context.tags || [],
text: context.purpose || '',
sortBy: 'usage',
sortDirection: 'desc',
limit: 5,
};
return this.searchTemplates(query);
}
/**
* Get all available categories
*/
getAvailableCategories() {
return Object.values(TemplateCategory);
}
/**
* Get category metadata
*/
getCategoryMetadata(category) {
const metadata = {
[TemplateCategory.MOTIVATIONAL]: {
name: 'Motivational',
description: 'Templates for motivational and inspiring quotes',
icon: '💪',
},
[TemplateCategory.INSPIRATIONAL]: {
name: 'Inspirational',
description: 'Templates for uplifting and inspirational quotes',
icon: '✨',
},
[TemplateCategory.BUSINESS]: {
name: 'Business',
description: 'Templates for business and professional quotes',
icon: '💼',
},
[TemplateCategory.PHILOSOPHICAL]: {
name: 'Philosophical',
description: 'Templates for deep philosophical thoughts',
icon: '🤔',
},
[TemplateCategory.LITERARY]: {
name: 'Literary',
description: 'Templates for literary and poetic quotes',
icon: '📖',
},
[TemplateCategory.SCIENTIFIC]: {
name: 'Scientific',
description: 'Templates for scientific and discovery quotes',
icon: '🔬',
},
[TemplateCategory.HISTORICAL]: {
name: 'Historical',
description: 'Templates for historical figures and events',
icon: '🏛️',
},
[TemplateCategory.HUMOR]: {
name: 'Humor',
description: 'Templates for humorous and witty quotes',
icon: '😄',
},
[TemplateCategory.WISDOM]: {
name: 'Wisdom',
description: 'Templates for timeless wisdom and advice',
icon: '🦉',
},
[TemplateCategory.LEADERSHIP]: {
name: 'Leadership',
description: 'Templates for leadership and management quotes',
icon: '👑',
},
[TemplateCategory.EDUCATION]: {
name: 'Education',
description: 'Templates for educational and learning quotes',
icon: '🎓',
},
[TemplateCategory.TECHNOLOGY]: {
name: 'Technology',
description: 'Templates for technology and innovation quotes',
icon: '💻',
},
[TemplateCategory.SPORTS]: {
name: 'Sports',
description: 'Templates for sports and athletic quotes',
icon: '🏆',
},
[TemplateCategory.CREATIVITY]: {
name: 'Creativity',
description: 'Templates for creative and artistic quotes',
icon: '🎨',
},
[TemplateCategory.CUSTOM]: {
name: 'Custom',
description: 'User-created custom templates',
icon: '🔧',
},
};
return metadata[category];
}
/**
* Export template as JSON
*/
async exportTemplate(templateId) {
const template = await this.getTemplate(templateId);
if (!template) {
throw new Error(`Template not found: ${templateId}`);
}
return JSON.stringify(template, null, 2);
}
/**
* Import template from JSON
*/
async importTemplate(jsonString) {
try {
const template = JSON.parse(jsonString);
// Convert date strings back to Date objects
template.metadata.createdAt = new Date(template.metadata.createdAt);
template.metadata.updatedAt = new Date(template.metadata.updatedAt);
// Validate imported template
const validation = this.validateTemplate(template);
if (!validation.isValid) {
throw new Error(`Imported template validation failed: ${validation.errors.map((e) => e.message).join(', ')}`);
}
// Generate new ID to avoid conflicts
template.metadata.id = `imported-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
await this.saveTemplate(template);
return template;
}
catch (error) {
logger.error('Failed to import template', error);
throw new Error(`Template import failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Clone an existing template
*/
async cloneTemplate(templateId, newName) {
const originalTemplate = await this.getTemplate(templateId);
if (!originalTemplate) {
throw new Error(`Template not found: ${templateId}`);
}
const clonedTemplate = JSON.parse(JSON.stringify(originalTemplate));
// Update metadata for clone
clonedTemplate.metadata.id = `clone-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
clonedTemplate.metadata.name = newName;
clonedTemplate.metadata.description = `Clone of ${originalTemplate.metadata.name}`;
clonedTemplate.metadata.createdAt = new Date();
clonedTemplate.metadata.updatedAt = new Date();
clonedTemplate.metadata.version = '1.0.0';
// Reset usage stats
clonedTemplate.metadata.usageStats = {
totalUses: 0,
lastUsed: new Date(),
};
await this.saveTemplate(clonedTemplate);
return clonedTemplate;
}
/**
* Get template statistics
*/
async getTemplateStatistics() {
const allTemplates = await this.searchTemplates({ includeDeprecated: false });
// Count by category
const byCategory = {};
Object.values(TemplateCategory).forEach((cat) => {
byCategory[cat] = 0;
});
allTemplates.forEach((template) => {
byCategory[template.metadata.category]++;
});
// Sort by usage
const mostUsed = [...allTemplates]
.sort((a, b) => (b.metadata.usageStats?.totalUses || 0) - (a.metadata.usageStats?.totalUses || 0))
.slice(0, 5);
// Sort by update date
const recentlyUpdated = [...allTemplates]
.sort((a, b) => b.metadata.updatedAt.getTime() - a.metadata.updatedAt.getTime())
.slice(0, 5);
return {
totalTemplates: allTemplates.length,
byCategory,
mostUsed,
recentlyUpdated,
};
}
}
// Export singleton instance
export const templateManager = TemplateManager.getInstance();
//# sourceMappingURL=templateManager.js.map