Skip to main content
Glama

Analytical MCP Server

decision_analysis.ts8.14 kB
import { z } from 'zod'; import { ValidationError, DataProcessingError } from '../utils/errors.js'; import { Logger } from '../utils/logger.js'; // Schema for the tool parameters export const decisionAnalysisSchema = z.object({ options: z.array(z.string()).describe('List of decision options to analyze'), criteria: z.array(z.string()).describe('List of criteria to evaluate options against'), weights: z .array(z.number()) .optional() .describe('Optional weights for each criterion (must match criteria length)'), }); // Interface for the function parameters export interface DecisionAnalysisParams { options: string[]; criteria: string[]; weights?: number[]; } // Tool implementation that accepts both a parameter object and individual parameters export async function decisionAnalysis( optionsOrParams: string[] | DecisionAnalysisParams, criteriaOrVoid?: string[], weights?: number[] ): Promise<string> { // Handle both parameter styles let options: string[]; let criteria: string[]; if (Array.isArray(optionsOrParams)) { // Old style with separate parameters options = optionsOrParams; criteria = criteriaOrVoid || []; } else { // New style with parameter object options = optionsOrParams.options; criteria = optionsOrParams.criteria; weights = optionsOrParams.weights; } try { Logger.debug('Starting decision analysis', { optionsCount: options.length, criteriaCount: criteria.length, weightsProvided: !!weights, }); // Validate inputs if (!options || !Array.isArray(options) || options.length === 0) { throw new ValidationError('ERR_1001', 'At least one option must be provided'); } if (!criteria || !Array.isArray(criteria) || criteria.length === 0) { throw new ValidationError('ERR_1001', 'At least one criterion must be provided'); } // Check for empty strings in options if (options.some((opt) => typeof opt !== 'string' || opt.trim() === '')) { throw new ValidationError('ERR_1001', 'All options must be non-empty strings'); } // Check for empty strings in criteria if (criteria.some((crit) => typeof crit !== 'string' || crit.trim() === '')) { throw new ValidationError('ERR_1001', 'All criteria must be non-empty strings'); } // Use equal weights if none provided const normalizedWeights = weights || criteria.map(() => 1 / criteria.length); // Validate weights length if (normalizedWeights.length !== criteria.length) { throw new ValidationError( 'ERR_1001', `Weights length (${normalizedWeights.length}) must match criteria length (${criteria.length})` ); } // Validate weights are numbers and sum to approximately 1 if (weights) { if (weights.some((w) => typeof w !== 'number' || isNaN(w))) { throw new ValidationError('ERR_1001', 'All weights must be valid numbers'); } const weightSum = weights.reduce((sum, w) => sum + w, 0); if (Math.abs(weightSum - 1) > 0.001 && Math.abs(weightSum - 100) > 0.001) { Logger.warn( `Weights don't sum to 1 or 100 (sum=${weightSum}). Proceeding with normalization.` ); } } try { // Generate random scores for demo purposes // In a real implementation, this would use real data or ask for user input const scores: number[][] = []; const pros: string[][] = []; const cons: string[][] = []; for (let i = 0; i < options.length; i++) { scores[i] = []; pros[i] = []; cons[i] = []; for (let j = 0; j < criteria.length; j++) { // Generate a random score between 1-10 const score = Math.floor(Math.random() * 10) + 1; scores[i][j] = score; // Generate pros and cons based on score if (score >= 7) { pros[i].push(`Strong in "${criteria[j]}"`); } else if (score >= 4) { // No strong pro or con } else { cons[i].push(`Weak in "${criteria[j]}"`); } } // Add a few more realistic pros and cons if (Math.random() > 0.5) { pros[i].push('Implementation is straightforward'); } if (Math.random() > 0.5) { cons[i].push('May require additional resources'); } if (Math.random() > 0.7) { pros[i].push('Aligned with organizational goals'); } if (Math.random() > 0.7) { cons[i].push('Potential regulatory challenges'); } } // Calculate weighted scores const weightedScores = options.map((option, i) => { return scores[i].reduce((sum, score, j) => { return sum + score * normalizedWeights[j]; }, 0); }); // Define interfaces for clear types interface RankedOption { option: string; score: number; pros: string[]; cons: string[]; } // Rank options with type safety const rankedOptions: RankedOption[] = options .map((option, i) => ({ option, score: weightedScores[i], pros: pros[i] || [], cons: cons[i] || [], })) .sort((a, b) => (b.score ?? 0) - (a.score ?? 0)); // Format output let result = `## Decision Analysis Results\n\n`; // Add ranked options result += `### Ranked Options\n\n`; rankedOptions.forEach((item, i) => { result += `**${i + 1}. ${item.option}** (Score: ${item.score.toFixed(2)})\n`; }); // Add details for each option result += `\n### Detailed Analysis\n\n`; rankedOptions.forEach((item) => { result += `#### ${item.option} (Score: ${item.score.toFixed(2)})\n\n`; // Pros result += `**Pros:**\n`; if (item.pros && item.pros.length > 0) { item.pros.forEach((pro) => { result += `- ${pro}\n`; }); } else { result += `- No significant advantages identified\n`; } // Cons result += `\n**Cons:**\n`; if (item.cons && item.cons.length > 0) { item.cons.forEach((con) => { result += `- ${con}\n`; }); } else { result += `- No significant disadvantages identified\n`; } result += `\n`; }); // Add recommendations const topOption = rankedOptions.length > 0 ? rankedOptions[0] : null; result += `### Recommendation\n\n`; if (topOption) { result += `Based on the analysis, **${topOption.option}** appears to be the strongest option with a score of ${topOption.score.toFixed(2)}.\n`; result += `Its key strengths include: ${topOption.pros && topOption.pros.length > 0 ? topOption.pros.join(', ') : 'No significant pros identified'}.\n`; if (rankedOptions.length > 1) { const runnerUp = rankedOptions[1]; result += `\nThe second-best option is **${runnerUp.option}** with a score of ${runnerUp.score.toFixed(2)}.\n`; } } else { result += `No options were available for ranking.\n`; } Logger.debug('Decision analysis completed successfully', { optionsAnalyzed: options.length, topScore: rankedOptions.length > 0 ? rankedOptions[0].score : 0, }); return result; } catch (error) { Logger.error('Error generating decision analysis results', error); throw new DataProcessingError( 'ERR_3001', `Failed to generate decision analysis: ${error instanceof Error ? error.message : String(error)}` ); } } catch (error) { // Ensure all errors are properly logged and categorized if (!(error instanceof ValidationError) && !(error instanceof DataProcessingError)) { Logger.error('Unexpected error in decision analysis', error); throw new DataProcessingError( 'ERR_3001', `Decision analysis failed: ${error instanceof Error ? error.message : String(error)}` ); } // Re-throw ValidationError and DataProcessingError throw error; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/quanticsoul4772/analytical-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server