@emmahyde/thinking-patterns
Version:
MCP server combining systematic thinking, mental models, debugging approaches, and stochastic algorithms for comprehensive cognitive pattern support
134 lines (133 loc) • 6.09 kB
JavaScript
import { BaseToolServer } from '../base/BaseToolServer.js';
import { SequentialThoughtSchema } from '../schemas/index.js';
import { boxed } from '../utils/index.js';
/**
* Sequential Thinking Server using thinking-patterns tools approach
* Extends BaseToolServer for standardized validation and error handling
*/
export class SequentialThinkingServer extends BaseToolServer {
constructor() {
super(SequentialThoughtSchema);
}
handle(validInput) {
return this.process(validInput);
}
/**
* Standardized process method for sequential thinking
* @param validInput - Validated thought data
* @returns Processed thought result
*/
process(validInput) {
// Format output using boxed utility
const formattedOutput = this.formatThoughtOutput(validInput);
// Log formatted output to console (suppress during tests)
if (process.env.NODE_ENV !== 'test' && process.env.JEST_WORKER_ID === undefined) {
console.error(formattedOutput);
}
return {
thoughtNumber: validInput.thoughtNumber,
totalThoughts: validInput.totalThoughts,
nextThoughtNeeded: validInput.nextThoughtNeeded,
thought: validInput.thought,
isRevision: validInput.isRevision || false,
revisesThought: validInput.revisesThought,
branchFromThought: validInput.branchFromThought,
branchId: validInput.branchId,
needsMoreThoughts: validInput.needsMoreThoughts,
currentStep: validInput.currentStep,
previousSteps: validInput.previousSteps,
remainingSteps: validInput.remainingSteps,
toolUsageHistory: validInput.toolUsageHistory,
status: 'success',
hasCurrentStep: !!validInput.currentStep,
hasPreviousSteps: !!validInput.previousSteps && validInput.previousSteps.length > 0,
hasRemainingSteps: !!validInput.remainingSteps && validInput.remainingSteps.length > 0,
hasToolUsageHistory: !!validInput.toolUsageHistory && validInput.toolUsageHistory.length > 0,
stage: this.determineStage(validInput.thoughtNumber, validInput.totalThoughts),
timestamp: new Date().toISOString(),
};
}
// Backward compatibility method for existing tests
processThought(input) {
return this.run(input);
}
formatThoughtOutput(data) {
const sections = {
'Thought': `${data.thoughtNumber}/${data.totalThoughts}`,
'Content': data.thought
};
if (data.isRevision && data.revisesThought) {
sections['Type'] = `REVISION (revising thought ${data.revisesThought})`;
}
else if (data.branchFromThought && data.branchId) {
sections['Type'] = `BRANCH (from thought ${data.branchFromThought}, ID: ${data.branchId})`;
}
else {
sections['Type'] = 'SEQUENTIAL';
}
// Current step information
if (data.currentStep) {
const step = data.currentStep;
sections['Current Step'] = [
`Description: ${step.stepDescription}`,
`Expected Outcome: ${step.expectedOutcome}`,
...(step.estimatedDuration ? [`Duration: ${step.estimatedDuration}`] : []),
...(step.complexityLevel ? [`Complexity: ${step.complexityLevel.toUpperCase()}`] : [])
];
if (step.recommendedTools.length > 0) {
sections['Recommended Tools'] = step.recommendedTools.map(tool => `• ${tool.toolName} (${(tool.confidence * 100).toFixed(0)}%): ${tool.rationale}`);
}
if (step.nextStepConditions.length > 0) {
sections['Next Step Conditions'] = step.nextStepConditions.map(condition => `• ${condition}`);
}
}
// Previous steps
if (data.previousSteps && data.previousSteps.length > 0) {
sections['Previous Steps'] = data.previousSteps.map((step, index) => `${index + 1}. ${step.stepDescription}`);
}
// Remaining steps
if (data.remainingSteps && data.remainingSteps.length > 0) {
sections['Remaining Steps'] = data.remainingSteps.map(step => `• ${step}`);
}
// Tool usage history
if (data.toolUsageHistory && data.toolUsageHistory.length > 0) {
sections['Tool Usage History'] = data.toolUsageHistory.map(usage => {
const effectiveness = usage.effectivenessScore ? ` (${(usage.effectivenessScore * 100).toFixed(0)}%)` : '';
return `• ${usage.toolName} at ${usage.usedAt}${effectiveness}`;
});
}
// Progress information
const progress = Math.round((data.thoughtNumber / data.totalThoughts) * 100);
sections['Progress'] = `${progress}% (${data.thoughtNumber}/${data.totalThoughts})`;
if (data.needsMoreThoughts) {
sections['Status'] = 'MORE THOUGHTS NEEDED';
}
else if (data.nextThoughtNeeded) {
sections['Status'] = 'NEXT THOUGHT NEEDED';
}
else {
sections['Status'] = 'SEQUENCE COMPLETE';
}
return boxed('💭 Sequential Thinking', sections);
}
determineStage(thoughtNumber, totalThoughts) {
// Handle edge cases first
if (totalThoughts === 1)
return 'final'; // Single thought is always final
if (thoughtNumber === 1 && totalThoughts > 1)
return 'initial';
if (thoughtNumber === totalThoughts)
return 'final';
// For sequences with 2 thoughts, first is initial, second is final
if (totalThoughts === 2) {
return thoughtNumber === 1 ? 'initial' : 'final';
}
// For longer sequences, use proportional logic
const progress = thoughtNumber / totalThoughts;
if (progress <= 0.33)
return 'initial';
if (progress >= 0.67)
return 'final';
return 'middle';
}
}