UNPKG

@trishchuk/ai-think-gate-mcp

Version:

Model Context Protocol (MCP) server that provides AI-powered thinking and code architecture tools

284 lines (283 loc) • 11.2 kB
import { BaseTool } from '../base-tool.js'; import { logService } from '../../services/logging-service.js'; import { DESCRIPTION } from './prompt.js'; import { getSequentialThinkingToolAnnotations } from '../tool-annotations.js'; import { ToolNames } from '../../../domain/constants.js'; /** * Sequential Thinking tool for step-by-step problem-solving */ export class SequentialThinkingTool extends BaseTool { constructor() { super(ToolNames.sequential_thinking, DESCRIPTION, { type: "object", properties: { thought: { type: "string", description: "Your current thinking step" }, nextThoughtNeeded: { type: "boolean", description: "Whether another thought step is needed" }, thoughtNumber: { type: "integer", description: "Current thought number", minimum: 1 }, totalThoughts: { type: "integer", description: "Estimated total thoughts needed", minimum: 1 }, isRevision: { type: "boolean", description: "Whether this revises previous thinking" }, revisesThought: { type: "integer", description: "Which thought is being reconsidered", minimum: 1 }, branchFromThought: { type: "integer", description: "Branching point thought number", minimum: 1 }, branchId: { type: "string", description: "Branch identifier" }, needsMoreThoughts: { type: "boolean", description: "If more thoughts are needed" } }, required: ["thought", "nextThoughtNeeded", "thoughtNumber", "totalThoughts"] }, getSequentialThinkingToolAnnotations()); this.thoughtHistory = []; this.branches = {}; } /** * Validate thought data structure */ validateThoughtData(input) { const data = input; if (!data.thought || typeof data.thought !== 'string') { throw new Error('Invalid thought: must be a string'); } if (!data.thoughtNumber || typeof data.thoughtNumber !== 'number') { throw new Error('Invalid thoughtNumber: must be a number'); } if (!data.totalThoughts || typeof data.totalThoughts !== 'number') { throw new Error('Invalid totalThoughts: must be a number'); } if (typeof data.nextThoughtNeeded !== 'boolean') { throw new Error('Invalid nextThoughtNeeded: must be a boolean'); } return { thought: data.thought, thoughtNumber: data.thoughtNumber, totalThoughts: data.totalThoughts, nextThoughtNeeded: data.nextThoughtNeeded, isRevision: data.isRevision, revisesThought: data.revisesThought, branchFromThought: data.branchFromThought, branchId: data.branchId, needsMoreThoughts: data.needsMoreThoughts, }; } /** * Format a thought for display */ formatThought(thoughtData) { const { thoughtNumber, totalThoughts, thought, isRevision, revisesThought, branchFromThought, branchId } = thoughtData; let prefix = ''; let context = ''; if (isRevision) { prefix = 'šŸ”„ Revision'; context = ` (revising thought ${revisesThought})`; } else if (branchFromThought) { prefix = '🌿 Branch'; context = ` (from thought ${branchFromThought}, ID: ${branchId})`; } else { prefix = 'šŸ’­ Thought'; context = ''; } const header = `${prefix} ${thoughtNumber}/${totalThoughts}${context}`; const border = '─'.repeat(Math.max(header.length, thought.length) + 4); return ` ā”Œ${border}┐ │ ${header} │ ā”œ${border}┤ │ ${thought.padEnd(border.length - 2)} │ ā””${border}ā”˜`; } /** * Execution of the SequentialThinking tool */ async execute(args) { logService.log(`Handling sequential_thinking tool with args: ${JSON.stringify(args).substring(0, 100)}...`); try { const validatedInput = this.validateThoughtData(args); // Adjust total thoughts if current thought exceeds total if (validatedInput.thoughtNumber > validatedInput.totalThoughts) { validatedInput.totalThoughts = validatedInput.thoughtNumber; } // Add thought to history this.thoughtHistory.push(validatedInput); // Handle branching if (validatedInput.branchFromThought && validatedInput.branchId) { if (!this.branches[validatedInput.branchId]) { this.branches[validatedInput.branchId] = []; } this.branches[validatedInput.branchId].push(validatedInput); } // Log the formatted thought const formattedThought = this.formatThought(validatedInput); logService.log(formattedThought); // Return the state information for the next step return { content: [{ type: "text", text: JSON.stringify({ thoughtNumber: validatedInput.thoughtNumber, totalThoughts: validatedInput.totalThoughts, nextThoughtNeeded: validatedInput.nextThoughtNeeded, branches: Object.keys(this.branches), thoughtHistoryLength: this.thoughtHistory.length }, null, 2) }] }; } catch (error) { logService.error("Error in sequential_thinking tool:", error); return this.formatError(`${error?.message || 'Unknown error'}`, "Invalid thought data provided. Please check the required parameters."); } } } // Export an instance of the tool for use export const sequentialThinkingTool = new SequentialThinkingTool(); /** * Reasoning Tools for step-by-step reasoning and analysis */ export class ReasoningTools extends BaseTool { constructor() { super(ToolNames.reasoning_tools, "A toolkit that provides step-by-step reasoning tools: Think and Analyze.", { type: "object", properties: { tool: { type: "string", description: "The reasoning tool to use (think or analyze)" }, title: { type: "string", description: "A concise title for this step" }, thought: { type: "string", description: "Your detailed thought for this step" }, action: { type: "string", description: "What you'll do based on this thought" }, confidence: { type: "number", description: "How confident you are about this thought (0.0 to 1.0)", minimum: 0.0, maximum: 1.0 }, result: { type: "string", description: "The outcome of the previous action" }, analysis: { type: "string", description: "Your analysis of the results" }, nextAction: { type: "string", description: "What to do next (continue, validate, or final_answer)" } }, required: ["tool", "title"] }); } /** * Execution of the ReasoningTools */ async execute(args) { logService.log(`Handling reasoning_tools with args: ${JSON.stringify(args).substring(0, 100)}...`); try { const data = args; const tool = data.tool; if (tool === "think") { return this.handleThink(data); } else if (tool === "analyze") { return this.handleAnalyze(data); } else { throw new Error("Invalid tool specified. Use 'think' or 'analyze'."); } } catch (error) { logService.error("Error in reasoning_tools:", error); return this.formatError(`${error?.message || 'Unknown error'}`, "Invalid reasoning tool data provided. Please check the required parameters."); } } /** * Handle the 'think' tool */ handleThink(data) { const { title, thought, action, confidence } = data; if (!title || typeof title !== "string") { throw new Error("Invalid title: must be a string"); } if (!thought || typeof thought !== "string") { throw new Error("Invalid thought: must be a string"); } if (confidence !== undefined && (typeof confidence !== "number" || confidence < 0 || confidence > 1)) { throw new Error("Invalid confidence: must be a number between 0.0 and 1.0"); } logService.log(`Thinking: ${title} - ${thought}`); return { content: [{ type: "text", text: `Thought recorded: ${title} - ${thought}` }] }; } /** * Handle the 'analyze' tool */ handleAnalyze(data) { const { title, result, analysis, nextAction, confidence } = data; if (!title || typeof title !== "string") { throw new Error("Invalid title: must be a string"); } if (!result || typeof result !== "string") { throw new Error("Invalid result: must be a string"); } if (!analysis || typeof analysis !== "string") { throw new Error("Invalid analysis: must be a string"); } if (!nextAction || typeof nextAction !== "string") { throw new Error("Invalid nextAction: must be a string"); } if (confidence !== undefined && (typeof confidence !== "number" || confidence < 0 || confidence > 1)) { throw new Error("Invalid confidence: must be a number between 0.0 and 1.0"); } logService.log(`Analyzing: ${title} - ${analysis}`); return { content: [{ type: "text", text: `Analysis recorded: ${title} - ${analysis}` }] }; } } // Export an instance of the tool for use export const reasoningTools = new ReasoningTools();