mcp-prompt-optimizer-local
Version:
Advanced cross-platform prompt optimization with MCP integration and 120+ optimization rules
781 lines (691 loc) • 30.2 kB
JavaScript
/**
* MCP Tool Definitions - Complete tool schema matching Python implementation
* Provides MCP-compatible tool definitions for Claude Desktop integration
* NOW WITH LICENSE VALIDATION
*/
// MCP Tool Definitions matching Python protocol_handler.py
const MCP_TOOLS = {
"optimize_prompt": {
"description": "Optimizes prompt text based on specified goals like clarity, conciseness, and effectiveness. Enhances prompts for better AI model performance.",
"inputSchema": {
"type": "object",
"properties": {
"prompt": {
"type": "string",
"description": "The prompt text to optimize."
},
"goals": {
"type": "array",
"items": { "type": "string" },
"description": "Optimization goals (e.g., 'clarity', 'conciseness').",
"default": ["clarity"]
},
"ai_context": {
"type": "string",
"enum": [
"human-communication",
"llm-interaction",
"image-generation",
"technical-automation",
"structured-output",
"code-generation",
"api-automation"
],
"description": "The context for the AI's task (e.g., generating an image, writing code)."
},
"target_ai_model": {
"type": "string",
"description": "Optional target AI model for optimization.",
"enum": ["claude", "gpt", "gemini", "general"]
}
},
"required": ["prompt"]
}
},
"get_quota_status": {
"description": "Check current license quota status and remaining optimizations",
"inputSchema": {
"type": "object",
"properties": {}
}
},
"list_saved_templates": {
"description": "Browse saved prompt optimization templates with optional category filtering",
"inputSchema": {
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["technical", "business", "creative", "educational", "analytical", "communication", "general"],
"description": "Filter templates by category"
},
"limit": {
"type": "integer",
"default": 20,
"minimum": 1,
"maximum": 100,
"description": "Maximum number of templates to return"
}
}
}
},
"search_templates": {
"description": "Search saved templates by content, tags, or metadata with relevance scoring",
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query text to match against template content and metadata"
},
"category": {
"type": "string",
"enum": ["technical", "business", "creative", "educational", "analytical", "communication", "general"],
"description": "Filter results by category"
},
"limit": {
"type": "integer",
"default": 10,
"minimum": 1,
"maximum": 50,
"description": "Maximum number of results to return"
}
},
"required": ["query"]
}
},
"get_template": {
"description": "Retrieve a specific template by ID with full metadata and analytics",
"inputSchema": {
"type": "object",
"properties": {
"template_id": {
"type": "string",
"description": "Unique template identifier to retrieve"
},
"include_metadata": {
"type": "boolean",
"default": true,
"description": "Include enhanced metadata and analytics in response"
}
},
"required": ["template_id"]
}
},
"get_template_stats": {
"description": "Get comprehensive statistics about saved template collection",
"inputSchema": {
"type": "object",
"properties": {
"detailed": {
"type": "boolean",
"default": false,
"description": "Include detailed analytics and usage patterns"
}
}
}
},
"use_template_as_base": {
"description": "Start new optimization using an existing template as foundation with optional modifications",
"inputSchema": {
"type": "object",
"properties": {
"template_id": {
"type": "string",
"description": "Template ID to use as starting point"
},
"modifications": {
"type": "string",
"description": "Optional additional requirements or modifications to apply"
},
"new_goals": {
"type": "array",
"items": {"type": "string"},
"description": "New optimization goals to apply (overrides template goals if provided)"
}
},
"required": ["template_id"]
}
}
};
/**
* MCP Tool Handler - Handles tool invocations WITH LICENSE VALIDATION
*/
class MCPToolHandler {
constructor(optimizer, logger = null) {
this.optimizer = optimizer;
this.logger = logger || this._createLogger();
this.templateSystem = new TemplateSystemMock(); // Mock for now
}
/**
* Get list of available tools
* @param {Object} options - Tool listing options
* @returns {Object} - MCP tools list response
*/
async getToolsList(options = {}) {
const toolsArray = [];
// Always include core optimization tool
toolsArray.push({
name: "optimize_prompt",
description: MCP_TOOLS.optimize_prompt.description,
inputSchema: MCP_TOOLS.optimize_prompt.inputSchema
});
// Always include quota status tool
toolsArray.push({
name: "get_quota_status",
description: MCP_TOOLS.get_quota_status.description,
inputSchema: MCP_TOOLS.get_quota_status.inputSchema
});
// Add template tools if system is available
const templateToolsEnabled = options.templateToolsEnabled || false;
if (templateToolsEnabled) {
for (const [toolName, toolDef] of Object.entries(MCP_TOOLS)) {
if (toolName !== "optimize_prompt" && toolName !== "get_quota_status") {
toolsArray.push({
name: toolName,
description: toolDef.description,
inputSchema: toolDef.inputSchema
});
}
}
}
this.logger.info(`Responding with ${toolsArray.length} available tools`);
return {
tools: toolsArray
};
}
/**
* Handle tool invocation
* @param {string} toolName - Name of tool to invoke
* @param {Object} toolArguments - Tool arguments
* @param {string} messageId - Message ID for response
* @returns {Object} - Tool response
*/
async handleToolInvoke(toolName, toolArguments, messageId = null) {
this.logger.info(`Handling tool invoke: '${toolName}' with args:`, toolArguments);
try {
switch (toolName) {
case "optimize_prompt":
return await this._handleOptimizePrompt(toolArguments, messageId);
case "get_quota_status":
return await this._handleGetQuotaStatus(toolArguments, messageId);
case "list_saved_templates":
return await this._handleListTemplates(toolArguments, messageId);
case "search_templates":
return await this._handleSearchTemplates(toolArguments, messageId);
case "get_template":
return await this._handleGetTemplate(toolArguments, messageId);
case "get_template_stats":
return await this._handleTemplateStats(toolArguments, messageId);
case "use_template_as_base":
return await this._handleUseTemplateBase(toolArguments, messageId);
default:
return this._createErrorResponse({
code: -32601,
message: `Tool '${toolName}' not found or not available.`
}, messageId);
}
} catch (error) {
this.logger.error(`Tool invocation failed for '${toolName}': ${error.message}`);
return this._createErrorResponse({
code: -32603,
message: `Internal error during tool invocation: ${error.constructor.name}`
}, messageId);
}
}
/**
* Handle optimize_prompt tool WITH LICENSE VALIDATION
* @param {Object} params - Tool parameters
* @param {string} messageId - Message ID
* @returns {Object} - Tool response
*/
async _handleOptimizePrompt(params, messageId) {
const prompt = params.prompt;
const goals = params.goals || ["clarity"];
const aiContext = params.ai_context || "llm-interaction";
this.logger.info(`Executing optimize_prompt: prompt length=${prompt?.length || 0}, goals=[${goals.join(', ')}]`);
// NEW: License validation before optimization
if (this.optimizer && this.optimizer.licenseManager) {
try {
const licenseResult = await this.optimizer.licenseManager.validateLicenseAndQuota();
if (!licenseResult.valid) {
return this._createErrorResponse({
code: -32001,
message: `License validation failed: ${licenseResult.error}`
}, messageId);
}
// Check quota for limited users
if (licenseResult.quota && !licenseResult.quota.unlimited && !licenseResult.quota.allowed) {
const resetTime = new Date(licenseResult.quota.resetsAt).toLocaleString();
return this._createErrorResponse({
code: -32000,
message: `Daily optimization limit (${licenseResult.quota.limit}) reached. Quota resets at ${resetTime}. Upgrade to Pro for unlimited optimizations.`
}, messageId);
}
this.logger.info(`✅ License validated. Proceeding with optimization.`);
} catch (licenseError) {
this.logger.error(`License validation error: ${licenseError.message}`);
return this._createErrorResponse({
code: -32001,
message: `License validation failed: ${licenseError.message}`
}, messageId);
}
}
try {
if (!this.optimizer) {
throw new Error("Optimizer not available");
}
// Use the advanced optimization method (which now includes license checking)
const result = await this.optimizer.optimizeForContext(prompt, aiContext, goals);
// Get updated quota status for response metadata
let quotaInfo = {};
try {
quotaInfo = await this.optimizer.checkQuotaStatus();
} catch (quotaError) {
this.logger.warn(`Could not get quota status: ${quotaError.message}`);
}
const toolResponse = {
content: [{
type: "text",
text: result.optimizedText
}],
metadata: {
appliedRules: result.appliedRules,
confidence: result.confidence,
processingTime: result.processingTime,
goalAchievementScores: result.goalAchievementScores,
quotaStatus: quotaInfo
}
};
this.logger.info(`Optimization successful: applied ${result.appliedRules.length} rules, confidence=${result.confidence?.toFixed(2) || 'N/A'}`);
return this._createResponse(toolResponse, messageId);
} catch (error) {
this.logger.error(`Optimization failed: ${error.message}`);
// Handle quota exceeded errors specifically
if (error.message.includes('Daily optimization limit')) {
return this._createErrorResponse({
code: -32000,
message: error.message
}, messageId);
}
return this._createErrorResponse({
code: -32603,
message: `Optimization process failed: ${error.message}`
}, messageId);
}
}
/**
* NEW: Handle get_quota_status tool
* @param {Object} params - Tool parameters
* @param {string} messageId - Message ID
* @returns {Object} - Tool response
*/
async _handleGetQuotaStatus(params, messageId) {
try {
if (!this.optimizer || !this.optimizer.licenseManager) {
return this._createErrorResponse({
code: -32603,
message: "License manager not available"
}, messageId);
}
const quotaStatus = await this.optimizer.checkQuotaStatus();
let responseText;
if (quotaStatus.error) {
responseText = `❌ Error checking quota: ${quotaStatus.error}`;
} else if (quotaStatus.unlimited) {
responseText = `✅ **${quotaStatus.tier.toUpperCase()} TIER** - Unlimited optimizations available`;
} else {
const resetTime = new Date(quotaStatus.resetsAt).toLocaleString();
responseText = `📊 **${quotaStatus.tier.toUpperCase()} TIER** - Daily Quota Status\n\n` +
`Used: ${quotaStatus.used}/${quotaStatus.limit} optimizations\n` +
`Remaining: ${quotaStatus.remaining}\n` +
`Resets: ${resetTime}\n\n` +
(quotaStatus.remaining === 0 ?
`⚠️ Daily limit reached. Consider upgrading to Pro for unlimited optimizations.` :
`✅ ${quotaStatus.remaining} optimizations remaining today.`);
}
return this._createResponse({
content: [{
type: "text",
text: responseText
}],
metadata: quotaStatus
}, messageId);
} catch (error) {
this.logger.error(`Get quota status failed: ${error.message}`);
return this._createErrorResponse({
code: -32603,
message: `Failed to get quota status: ${error.message}`
}, messageId);
}
}
/**
* Handle list_saved_templates tool (mock implementation)
* @param {Object} params - Tool parameters
* @param {string} messageId - Message ID
* @returns {Object} - Tool response
*/
async _handleListTemplates(params, messageId) {
const category = params.category;
const limit = params.limit || 20;
// Mock template data
const mockTemplates = this.templateSystem.getMockTemplates(category, limit);
const responseText = mockTemplates.length > 0
? this._formatTemplateList(mockTemplates, category)
: `No saved templates found${category ? ` in category '${category}'` : ''}.`;
return this._createResponse({
content: [{
type: "text",
text: responseText
}]
}, messageId);
}
/**
* Handle search_templates tool (mock implementation)
* @param {Object} params - Tool parameters
* @param {string} messageId - Message ID
* @returns {Object} - Tool response
*/
async _handleSearchTemplates(params, messageId) {
const query = params.query;
const category = params.category;
const limit = params.limit || 10;
if (!query) {
return this._createErrorResponse({
code: -32602,
message: "Search query required."
}, messageId);
}
// Mock search results
const searchResults = this.templateSystem.searchMockTemplates(query, category, limit);
const responseText = searchResults.length > 0
? this._formatSearchResults(searchResults, query)
: `No templates found for '${query}'${category ? ` in '${category}'` : ''}.`;
return this._createResponse({
content: [{
type: "text",
text: responseText
}]
}, messageId);
}
/**
* Handle get_template tool (mock implementation)
* @param {Object} params - Tool parameters
* @param {string} messageId - Message ID
* @returns {Object} - Tool response
*/
async _handleGetTemplate(params, messageId) {
const templateId = params.template_id;
const includeMeta = params.include_metadata !== false;
if (!templateId) {
return this._createErrorResponse({
code: -32602,
message: "template_id required."
}, messageId);
}
// Mock template retrieval
const template = this.templateSystem.getMockTemplate(templateId);
if (!template) {
return this._createErrorResponse({
code: -32000,
message: `Template '${templateId}' not found`
}, messageId);
}
const responseText = this._formatTemplateDetails(template, includeMeta);
return this._createResponse({
content: [{
type: "text",
text: responseText
}]
}, messageId);
}
/**
* Handle get_template_stats tool (mock implementation)
* @param {Object} params - Tool parameters
* @param {string} messageId - Message ID
* @returns {Object} - Tool response
*/
async _handleTemplateStats(params, messageId) {
const stats = this.templateSystem.getMockStats();
const responseText = this._formatTemplateStats(stats);
return this._createResponse({
content: [{
type: "text",
text: responseText
}]
}, messageId);
}
/**
* Handle use_template_as_base tool (mock implementation)
* @param {Object} params - Tool parameters
* @param {string} messageId - Message ID
* @returns {Object} - Tool response
*/
async _handleUseTemplateBase(params, messageId) {
const templateId = params.template_id;
const modifications = params.modifications || "";
const newGoals = params.new_goals || [];
if (!templateId) {
return this._createErrorResponse({
code: -32602,
message: "template_id required."
}, messageId);
}
// Mock template retrieval
const baseTemplate = this.templateSystem.getMockTemplate(templateId);
if (!baseTemplate) {
return this._createErrorResponse({
code: -32000,
message: `Base template '${templateId}' not found.`
}, messageId);
}
// Combine base template with modifications
const finalPrompt = baseTemplate.optimized_prompt +
(modifications ? `\n\nAdditional requirements:\n${modifications}` : "");
const finalGoals = newGoals.length > 0 ? newGoals : baseTemplate.optimization_goals;
// Run optimization (this will also handle license validation)
try {
const result = await this.optimizer.optimizeForContext(finalPrompt, "llm-interaction", finalGoals);
return this._createResponse({
content: [{
type: "text",
text: result.optimizedText
}],
metadata: {
baseTemplate: templateId,
appliedRules: result.appliedRules,
confidence: result.confidence
}
}, messageId);
} catch (error) {
// Handle quota errors specifically
if (error.message.includes('Daily optimization limit')) {
return this._createErrorResponse({
code: -32000,
message: error.message
}, messageId);
}
return this._createErrorResponse({
code: -32603,
message: `Failed to optimize using template base: ${error.message}`
}, messageId);
}
}
// Helper methods for formatting responses
_formatTemplateList(templates, category) {
const header = `Found ${templates.length} templates${category ? ` for '${category}'` : ''}:\n\n`;
const templateList = templates.map(tpl =>
`**${tpl.category} | ${tpl.complexity}**\n` +
`ID: \`${tpl.id.slice(0, 8)}\` | Confidence: ${tpl.confidence_score.toFixed(2)} | Uses: ${tpl.usage_stats.total_uses}\n` +
`Original: "${tpl.original_prompt.slice(0, 80)}..."\n` +
`Optimized: "${tpl.optimized_prompt.slice(0, 80)}..."`
).join('\n---\n');
return header + templateList + "\n\nUse `get_template` with ID for full details.";
}
_formatSearchResults(results, query) {
const header = `Found ${results.length} templates for '${query}':\n\n`;
const resultList = results.map(res =>
`**Relevance: ${res.relevance_score.toFixed(2)} | ${res.category}**\n` +
`ID: \`${res.id.slice(0, 8)}\` | Tags: ${res.tags.slice(0, 3).join(', ')}\n` +
`Original: "${res.original_prompt.slice(0, 70)}..."\n` +
`Optimized: "${res.optimized_prompt.slice(0, 70)}..."`
).join('\n---\n');
return header + resultList + "\n\nUse `get_template` with ID for full details.";
}
_formatTemplateDetails(template, includeMeta) {
let details = `**Template: ${template.id.slice(0, 8)}**\n\n` +
`**Category:** ${template.metadata.primary_category} | **Complexity:** ${template.metadata.complexity_level}\n` +
`**Confidence:** ${template.confidence_score.toFixed(2)} | **Uses:** ${template.usage_stats.total_uses}\n` +
`**Goals:** ${template.optimization_goals.join(', ')}\n\n` +
`**Original:**\n\`\`\`\n${template.original_prompt}\n\`\`\`\n\n` +
`**Optimized:**\n\`\`\`\n${template.optimized_prompt}\n\`\`\`\n`;
if (includeMeta) {
details += `\n**Use Cases:** ${template.metadata.use_cases.join(', ')}\n` +
`**Tags:** ${template.metadata.searchable_tags.join(', ')}\n`;
}
details += "\nUse `use_template_as_base` with this ID to start new optimization.";
return details;
}
_formatTemplateStats(stats) {
return `**Template Statistics** (${new Date().toISOString().split('T')[0]})\n\n` +
`Total: ${stats.total_templates}\nActive: ${stats.active_templates}\n` +
`Average Confidence: ${stats.average_confidence.toFixed(2)}\n` +
`Total Usage: ${stats.total_usage}\n\n` +
`**Top Categories:**\n${Object.entries(stats.categories).map(([cat, count]) =>
` - ${cat}: ${count}`).join('\n')}`;
}
_createResponse(result, messageId) {
return {
jsonrpc: "2.0",
result: result,
id: messageId
};
}
_createErrorResponse(error, messageId) {
return {
jsonrpc: "2.0",
error: error,
id: messageId
};
}
_createLogger() {
return {
debug: (msg) => console.log(`[DEBUG] ${msg}`),
info: (msg) => console.log(`[INFO] ${msg}`),
warn: (msg) => console.warn(`[WARN] ${msg}`),
error: (msg) => console.error(`[ERROR] ${msg}`)
};
}
}
/**
* Mock Template System for development/testing
*/
class TemplateSystemMock {
constructor() {
this.mockTemplates = [
{
id: "tpl_001_business_proposal",
category: "business",
complexity: "intermediate",
confidence_score: 0.87,
optimization_goals: ["structure", "professionalism", "actionability"],
original_prompt: "write a business proposal",
optimized_prompt: "Create a comprehensive business proposal with the following structure:\n\n## Executive Summary\n## Problem Statement\n## Proposed Solution\n## Implementation Timeline\n## Budget and Resources\n## Success Metrics and ROI",
metadata: {
primary_category: "business",
complexity_level: "intermediate",
use_cases: ["funding", "project approval", "client proposals"],
searchable_tags: ["business", "proposal", "structure", "professional"]
},
usage_stats: {
total_uses: 23,
average_rating: 4.2,
last_used: "2024-09-10"
},
tags: ["business", "proposal", "structure"],
relevance_score: 0.95
},
{
id: "tpl_002_technical_debug",
category: "technical",
complexity: "advanced",
confidence_score: 0.92,
optimization_goals: ["structure", "technical-precision", "actionability"],
original_prompt: "help debug my code",
optimized_prompt: "I need help debugging this code issue:\n\n**Problem Description:**\nMy code is experiencing an error that needs resolution.\n\n**Please provide:**\n1. Root cause analysis\n2. Step-by-step debugging approach\n3. Corrected code with explanations\n4. Best practices to prevent similar issues",
metadata: {
primary_category: "technical",
complexity_level: "advanced",
use_cases: ["debugging", "code review", "error resolution"],
searchable_tags: ["debug", "code", "technical", "troubleshoot"]
},
usage_stats: {
total_uses: 45,
average_rating: 4.6,
last_used: "2024-09-12"
},
tags: ["technical", "debug", "code"],
relevance_score: 0.88
}
];
}
getMockTemplates(category = null, limit = 20) {
let filtered = this.mockTemplates;
if (category) {
filtered = filtered.filter(t => t.category === category);
}
return filtered.slice(0, limit);
}
searchMockTemplates(query, category = null, limit = 10) {
let results = this.mockTemplates.filter(t => {
const matchesQuery = t.original_prompt.toLowerCase().includes(query.toLowerCase()) ||
t.optimized_prompt.toLowerCase().includes(query.toLowerCase()) ||
t.tags.some(tag => tag.toLowerCase().includes(query.toLowerCase()));
const matchesCategory = !category || t.category === category;
return matchesQuery && matchesCategory;
});
// Add relevance scores based on query match
results = results.map(t => ({
...t,
relevance_score: this._calculateRelevance(t, query)
}));
results.sort((a, b) => b.relevance_score - a.relevance_score);
return results.slice(0, limit);
}
getMockTemplate(templateId) {
return this.mockTemplates.find(t => t.id === templateId || t.id.startsWith(templateId));
}
getMockStats() {
return {
total_templates: this.mockTemplates.length,
active_templates: this.mockTemplates.length,
archived_templates: 0,
average_confidence: 0.895,
total_usage: this.mockTemplates.reduce((sum, t) => sum + t.usage_stats.total_uses, 0),
categories: {
business: 1,
technical: 1,
creative: 0,
academic: 0
}
};
}
_calculateRelevance(template, query) {
const queryLower = query.toLowerCase();
let score = 0;
// Original prompt match
if (template.original_prompt.toLowerCase().includes(queryLower)) score += 0.4;
// Tag matches
const tagMatches = template.tags.filter(tag =>
tag.toLowerCase().includes(queryLower)).length;
score += tagMatches * 0.2;
// Category match
if (template.category.toLowerCase().includes(queryLower)) score += 0.3;
return Math.min(score, 1.0);
}
}
module.exports = {
MCP_TOOLS,
MCPToolHandler,
TemplateSystemMock
};