@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
JavaScript
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();