mcp-product-manager
Version:
MCP Orchestrator for task and project management with web interface
169 lines • 8.23 kB
JavaScript
/**
* 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