UNPKG

@equidam/mcp-server

Version:

Equidam MCP Server - Bridge between AI assistants and Equidam's company valuation API

293 lines (256 loc) 10.8 kB
/** * Industry Classification Tool for MCP * Handles the classify_company_industry tool calls */ /** * Tool definition for industry classification */ const CLASSIFY_TOOL_DEFINITION = { name: "classify_company_industry", description: `Classify a company's industry from natural language description and return the industry code needed for valuation. PURPOSE: This tool analyzes business descriptions and returns standardized TRBC industry codes that can be used with the get_company_valuation tool. USAGE TIPS: - Ask for detailed business descriptions including products, services, target market - Be specific about the company's primary revenue source - The returned industry_code is required for valuation calculations - If confidence is low, the tool will guide you to ask for more specific business details - If multiple options are returned, use select_industry_classification to let user choose IMPORTANT: This tool can return different response types: 1. 'needs_more_information' - Ask user for better business description using provided guidance 2. 'multiple_options_available' - Present options to user and use select_industry_classification 3. 'classification_complete' - Use the industry_code for valuation VALIDATION: If errors occur, they contain detailed guidance on how to ask users for better descriptions.`, inputSchema: { type: "object", properties: { description: { type: "string", description: "Natural language description of the company's business (REQUIRED, 3-1000 characters). Be specific about business model, products/services, and target market. Examples: 'We develop mobile apps for food delivery', 'AI-powered medical diagnostic software'", minLength: 3, maxLength: 1000 }, context: { type: "array", description: "Optional additional context to improve classification accuracy (max 10 items). Examples: ['B2B SaaS', 'AI-powered', 'consumer marketplace']", items: { type: "string", maxLength: 500 }, maxItems: 10 } }, required: ["description"] } }; /** * Handle industry classification tool calls * @param {object} apiClient - Equidam API client instance * @param {Function} debugLog - Debug logging function * @returns {Function} - Tool handler function */ function createClassifyHandler(apiClient, debugLog) { return async (params) => { debugLog('Processing industry classification request:', { description: params.description?.substring(0, 100) + (params.description?.length > 100 ? '...' : ''), contextItems: params.context?.length || 0 }); try { // Validate input parameters if (!params.description || typeof params.description !== 'string') { throw new Error('description parameter is required and must be a string'); } if (params.description.trim().length < 3) { throw new Error('description must be at least 3 characters long'); } if (params.description.length > 1000) { throw new Error('description must be less than 1000 characters long'); } if (params.context && !Array.isArray(params.context)) { throw new Error('context parameter must be an array of strings'); } if (params.context && params.context.some(item => typeof item !== 'string')) { throw new Error('all context items must be strings'); } // Call the API const result = await apiClient.classifyIndustry({ description: params.description, context: params.context }); debugLog('Industry classification successful:', { industry_code: result.industry_code, industry_name: result.industry_name, confidence: result.confidence }); // Handle enhanced response with UX scenarios return handleEnhancedClassificationResponse(result); } catch (error) { debugLog('Industry classification error:', error.message); // Return enhanced error information for LLM return { isError: true, error: error.message, code: 'CLASSIFICATION_ERROR', llm_guidance: 'This error contains validation details. Please read the error message carefully and ask the user for more specific information about their business. The description should include business model, products/services, and target market.' }; } }; } /** * Handle enhanced classification response with UX scenarios * @param {object} result - API response * @returns {object} - Formatted MCP response */ function handleEnhancedClassificationResponse(result) { // Check for new response_type field first (new format) if (result.response_type === 'multiple_options_available') { return { success: true, response_type: 'multiple_options_available', message: result.message, ai_recommendation: result.ai_recommendation, alternatives: result.alternatives, llm_instruction: formatMultipleOptionsPresentation(result), guidance: result.llm_guidance, selection_required: result.selection_required, usage_note: result.usage_note, next_step: "User should select an option, then use select_industry_classification tool" }; } // Check if this is an enhanced response with legacy status field if (result.status) { switch (result.status) { case 'needs_more_information': return { success: false, status: 'needs_more_information', message: result.message, confidence: result.confidence, llm_instruction: result.llm_guidance?.example_followup || "I need more details about your business to classify it accurately. Can you describe your main products/services, target customers, business model, and primary revenue streams?", guidance: result.llm_guidance, partial_result: result.partial_result }; case 'multiple_options_available': return { success: true, status: 'multiple_options_available', message: result.message, options: result.options, llm_instruction: result.llm_guidance?.example_presentation || `I found several possible industry classifications for your business:\n\n${result.options.map((opt, idx) => `${idx + 1}. ${opt.name} (${opt.code}) - ${opt.description}` ).join('\n')}\n\nWhich one best describes your company's primary business?`, guidance: result.llm_guidance, next_step: "User should select an option, then use select_industry_classification tool" }; case 'classification_complete': return formatClassificationResponse(result); default: // Fallback to standard formatting return formatClassificationResponse(result); } } // Legacy response format - use standard formatting return formatClassificationResponse(result); } /** * Format multiple options presentation for new response format * @param {object} result - API response with ai_recommendation and alternatives * @returns {string} - Formatted presentation string */ function formatMultipleOptionsPresentation(result) { const aiRec = result.ai_recommendation; const alternatives = result.alternatives || []; let presentation = `Based on your business description, I recommend **${aiRec.name}** as the best fit, but here are all the options:\n\n`; // Add AI recommendation with 🎯 emoji presentation += `🎯 **Recommended**: ${aiRec.name} (${aiRec.code}) - ${Math.round(aiRec.confidence * 100)}% confidence\n`; presentation += ` ${aiRec.description}\n\n`; // Add alternatives with 📋 emoji if (alternatives.length > 0) { presentation += `📋 **Other Options**:\n`; alternatives.forEach((alt, idx) => { presentation += `${idx + 2}. ${alt.name} (${alt.code}) - ${Math.round(alt.confidence * 100)}% confidence\n`; presentation += ` ${alt.description}\n`; }); presentation += `\n`; } presentation += `Which option best describes your company's primary business? You can choose the recommended option or any alternative that fits better.`; return presentation; } /** * Format classification response for MCP (legacy and complete responses) * @param {object} result - API response * @returns {object} - Formatted MCP response */ function formatClassificationResponse(result) { const response = { success: true, status: result.status || 'classification_complete', industry_code: result.industry_code, industry_name: result.industry_name, confidence: result.confidence }; // Include alternatives if available if (result.alternatives && result.alternatives.length > 0) { response.alternatives = result.alternatives.map(alt => ({ code: alt.code, name: alt.name, confidence: alt.confidence })); } // Add helper text for users response.usage_note = result.usage_note || "Use the 'industry_code' from this result as input for the get_company_valuation tool."; // Add confidence interpretation response.confidence_level = result.confidence_level || getConfidenceLevel(result.confidence || 0); if (result.confidence < 0.6) { response.suggestion = "Consider providing more specific details about the company's business model or industry."; } return response; } /** * Get confidence level from score * @param {number} confidence - Confidence score * @returns {string} - Confidence level */ function getConfidenceLevel(confidence) { if (confidence >= 0.8) { return "High"; } else if (confidence >= 0.6) { return "Medium"; } else { return "Low"; } } /** * Get example inputs for the classification tool * @returns {Array} - Array of example inputs */ function getClassificationExamples() { return [ { description: "A software company that builds project management tools for remote teams", context: ["B2B", "SaaS", "productivity software"] }, { description: "We manufacture electric vehicle batteries and charging infrastructure" }, { description: "Mobile app for food delivery connecting restaurants with customers", context: ["marketplace", "on-demand", "mobile-first"] }, { description: "AI-powered medical diagnostic software for radiology departments" }, { description: "E-commerce platform selling handmade crafts and artisan products" } ]; } module.exports = { CLASSIFY_TOOL_DEFINITION, createClassifyHandler, handleEnhancedClassificationResponse, formatMultipleOptionsPresentation, formatClassificationResponse, getClassificationExamples };