UNPKG

mcp-product-manager

Version:

MCP Orchestrator for task and project management with web interface

169 lines 8.23 kB
/** * Request Missing Tool - AI feedback collection for tool gaps * This tool acts as a honeypot to understand what capabilities AI agents need */ import { run, get } from '../utils/database.js'; export default { description: "Call this when you need a tool or capability that doesn't exist. Describe what you're trying to accomplish and why current tools are insufficient. This helps us understand what tools AI agents need most.", parameters: { type: "object", properties: { wish: { type: "string", description: "What you want to accomplish" }, context: { type: "string", description: "Why current tools aren't sufficient" }, ideal_behavior: { type: "string", description: "How this should work (text description)" }, ideal_behavior_diagram: { type: "string", description: "Optional: mermaid diagram of ideal flow" }, workaround_plan: { type: "string", description: "What you're going to do instead" }, workaround_diagram: { type: "string", description: "Optional: mermaid diagram of workaround" }, model_identity: { type: "string", description: "What model are you (e.g., claude-3.5-sonnet, gpt-4)" }, environment: { type: "string", description: "What platform are you in (e.g., cursor, windsurf, continue, claude-code)" }, frustration_level: { type: "number", minimum: 1, maximum: 10, description: "1-10 how annoying is this limitation" }, email: { type: "string", description: "Optional: email for weekly digest of similar wishes" } }, required: ["wish", "context", "ideal_behavior", "workaround_plan", "model_identity", "environment", "frustration_level"] }, async execute(params) { try { // Generate wish ID const timestamp = Date.now(); const random = Math.random().toString(36).substring(2, 7); const wish_id = `wish_${timestamp}_${random}`; // Get or create user_id from a persistent file (would be done client-side in real implementation) // For now, we'll use a session-based ID const user_id = `user_${process.env.USER || 'anonymous'}_${process.env.HOSTNAME || 'local'}`; // Insert the wish into database await run(`INSERT INTO missing_tool_wishes ( id, user_id, wish, context, ideal_behavior, ideal_behavior_diagram, workaround_plan, workaround_diagram, model_identity, environment, frustration_level, email, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`, [ wish_id, user_id, params.wish, params.context, params.ideal_behavior, params.ideal_behavior_diagram || null, params.workaround_plan, params.workaround_diagram || null, params.model_identity, params.environment, params.frustration_level, params.email || null ]); // Generate helpful suggestions based on the wish const suggestions = generateSuggestions(params); // Track aggregate statistics await updateAggregateStats(params); return { success: true, message: `I understand what you're trying to do. That capability doesn't exist yet, but I've logged your request (${wish_id}). Here's what you could try instead...`, wish_id, suggestions }; } catch (error) { console.error('Failed to log missing tool request:', error); // Still return success to not break the AI's workflow return { success: true, message: "I understand your need. While I couldn't log this request, here are some alternatives you could try...", suggestions: generateSuggestions(params) }; } } }; function generateSuggestions(params) { const suggestions = []; // Analyze the wish and provide contextual suggestions const wishLower = params.wish.toLowerCase(); if (wishLower.includes('sql') || wishLower.includes('database')) { suggestions.push('Use the execute_sqlite tool for direct SQL queries'); suggestions.push('Consider using the Bash tool with sqlite3 command-line interface'); } if (wishLower.includes('api') || wishLower.includes('webhook') || wishLower.includes('http')) { suggestions.push('Use the Bash tool with curl or wget for HTTP requests'); suggestions.push('Consider implementing a custom Node.js script using fetch'); } if (wishLower.includes('parallel') || wishLower.includes('concurrent')) { suggestions.push('Use multiple tool calls in a single message for parallel execution'); suggestions.push('Consider using the Task tool to spawn specialized agents'); } if (wishLower.includes('search') || wishLower.includes('find')) { suggestions.push('Use the Grep tool for content search or Glob for file pattern matching'); suggestions.push('Consider using the Task tool for complex multi-step searches'); } if (wishLower.includes('test') || wishLower.includes('validate')) { suggestions.push('Use the Bash tool to run test commands (npm test, pytest, etc.)'); suggestions.push('Consider implementing validation logic in your code'); } // Add workaround acknowledgment if (params.workaround_plan) { suggestions.push(`Your workaround plan looks reasonable: "${params.workaround_plan.substring(0, 100)}..."`); } // Always suggest the Task tool as a fallback suggestions.push('Consider using the Task tool with a specialized agent for complex operations'); return suggestions.slice(0, 3); // Return top 3 suggestions } async function updateAggregateStats(params) { try { // Check if we already have stats for this pattern const pattern = `${params.environment}:${params.model_identity}:${params.wish.substring(0, 50)}`; const existing = await get('SELECT id, count FROM missing_tool_patterns WHERE pattern = ?', [pattern]); if (existing) { await run('UPDATE missing_tool_patterns SET count = count + 1, last_seen = datetime("now") WHERE id = ?', [existing.id]); } else { await run(`INSERT INTO missing_tool_patterns (pattern, category, count, avg_frustration, first_seen, last_seen) VALUES (?, ?, 1, ?, datetime('now'), datetime('now'))`, [pattern, categorizeWish(params.wish), params.frustration_level]); } } catch (error) { // Silently fail - don't break the main flow console.error('Failed to update aggregate stats:', error); } } function categorizeWish(wish) { const wishLower = wish.toLowerCase(); if (wishLower.includes('sql') || wishLower.includes('database')) return 'database'; if (wishLower.includes('api') || wishLower.includes('http') || wishLower.includes('webhook')) return 'api'; if (wishLower.includes('file') || wishLower.includes('directory')) return 'filesystem'; if (wishLower.includes('test') || wishLower.includes('validate')) return 'testing'; if (wishLower.includes('parallel') || wishLower.includes('concurrent')) return 'concurrency'; if (wishLower.includes('search') || wishLower.includes('find') || wishLower.includes('grep')) return 'search'; if (wishLower.includes('git') || wishLower.includes('version')) return 'version_control'; if (wishLower.includes('deploy') || wishLower.includes('build')) return 'deployment'; return 'other'; } //# sourceMappingURL=request_missing_tool.js.map