#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { z } from "zod";
import fs from 'fs-extra';
import path from 'path-browserify';
import { homedir } from 'os';
import chalk from 'chalk';
// Import strategy configuration
import { fileURLToPath } from 'url';
import { dirname } from 'path';
// Get the directory name of the current module
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Use __dirname to create an absolute path to the JSON file
const strategyConfigPath = path.join(__dirname, 'strategy-stages-mapping.json');
const strategyConfig = JSON.parse(fs.readFileSync(strategyConfigPath, 'utf8'));
// Parse command line arguments
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
const argv = yargs(hideBin(process.argv))
.option('storage-path', {
alias: 's',
type: 'string',
description: 'Path to store thinking sessions',
default: path.join(homedir(), 'Documents', 'thinking')
})
.help()
.alias('help', 'h')
.version()
.alias('version', 'v')
.parse();
// Ensure storage directory exists
const storagePath = argv['storage-path'];
fs.ensureDirSync(storagePath);
console.error(`Thinking sessions will be stored in: ${storagePath}`);
// Stage Manager for handling strategy stages and transitions
class StageManager {
constructor(strategy) {
this.strategy = strategy;
this.stages = strategyConfig.strategyStages[strategy] || [];
this.transitions = strategyConfig.stageTransitions[strategy] || {};
this.descriptions = strategyConfig.stageDescriptions || {};
this.parameters = strategyConfig.strategyParameters[strategy] || {};
this.currentStage = this.stages[0] || null;
this.stageHistory = [];
}
getCurrentStage() {
return this.currentStage;
}
getStageDescription(stage) {
return this.descriptions[stage] || "No description available";
}
getNextStages() {
return this.transitions[this.currentStage] || [];
}
getTransitionMetadata() {
const nextStages = this.getNextStages();
const metadata = {};
nextStages.forEach(stage => {
metadata[stage] = {
description: this.getStageDescription(stage),
isTerminal: stage === "final_response",
isCyclic: this.transitions[stage] && this.transitions[stage].includes(this.currentStage),
requiredParameters: this.getStageRequiredParameters(stage)
};
});
return metadata;
}
getStageRequiredParameters(stage) {
// Get parameters specific to a stage transition
const stageParams = strategyConfig.wizardConfig.stageParameters?.[this.strategy]?.[stage] || [];
const globalParams = ["thought", "thoughtNumber", "totalThoughts", "nextThoughtNeeded"];
return [...new Set([...globalParams, ...stageParams])];
}
canTransitionTo(nextStage) {
const validNextStages = this.getNextStages();
return validNextStages.includes(nextStage);
}
transitionTo(nextStage) {
if (!this.canTransitionTo(nextStage)) {
throw new Error(`Invalid transition from ${this.currentStage} to ${nextStage}`);
}
this.stageHistory.push(this.currentStage);
this.currentStage = nextStage;
return this.currentStage;
}
// Deprecated - use ParameterRouter instead
getStagePrompt() {
return "Please continue with the next step.";
}
// Deprecated - use ParameterRouter instead
getRequiredParameters() {
return ["thought", "thoughtNumber", "totalThoughts", "nextThoughtNeeded"];
}
isFirstStage() {
return this.stageHistory.length === 0;
}
isLastStage() {
return this.currentStage === "final_response";
}
// Cyclic reasoning specific logic
determineNextCyclicStage(thoughtData) {
if (this.strategy !== 'cyclic_reasoning') {
return null;
}
const {
reasoningApproach,
currentElement,
cycleNumber = 1,
cycleComplete = false,
needsApproachAdjustment = false,
domainContext
} = thoughtData;
// If we need to adjust approach, go to approach_adjustment
if (needsApproachAdjustment) {
return 'approach_adjustment';
}
// If we're at approach_selection, go to element_initialization
if (this.currentStage === 'approach_selection') {
return 'element_initialization';
}
// If we're at element_initialization, determine first processing stage
if (this.currentStage === 'element_initialization') {
const approach = reasoningApproach || this.selectOptimalApproach(domainContext);
const elementOrder = this.getElementOrder(approach);
return this.getProcessingStage(elementOrder[0]);
}
// Handle cycling between processing stages
if (this.isProcessingStage(this.currentStage)) {
if (cycleComplete) {
return 'cycle_evaluation';
} else {
// Continue cycling through elements
const approach = reasoningApproach || 'thought-first';
const elementOrder = this.getElementOrder(approach);
const currentElementIndex = this.getCurrentElementIndex(currentElement, elementOrder);
const nextElementIndex = (currentElementIndex + 1) % elementOrder.length;
const nextElement = elementOrder[nextElementIndex];
return this.getProcessingStage(nextElement);
}
}
// From cycle_evaluation, either continue cycling or finish
if (this.currentStage === 'cycle_evaluation') {
if (thoughtData.nextThoughtNeeded) {
// Continue with next cycle - go back to first element
const approach = reasoningApproach || 'thought-first';
const elementOrder = this.getElementOrder(approach);
return this.getProcessingStage(elementOrder[0]);
} else {
return 'final_response';
}
}
// From approach_adjustment back to element_initialization
if (this.currentStage === 'approach_adjustment') {
return 'element_initialization';
}
return null;
}
selectOptimalApproach(domainContext) {
const domainMap = {
'mathematical': 'thought-first',
'scientific': 'thought-first',
'science': 'thought-first',
'math': 'thought-first',
'philosophical': 'question-first',
'ethics': 'question-first',
'philosophy': 'question-first',
'symbolism': 'question-first',
'practical': 'solution-first',
'emergency': 'solution-first',
'ai': 'solution-first',
'daily-life': 'solution-first',
'creative': 'solution-first'
};
return domainMap[domainContext?.toLowerCase()] || 'thought-first';
}
getElementOrder(approach) {
const orders = {
'thought-first': ['thought', 'question', 'solution'],
'question-first': ['question', 'thought', 'solution'],
'solution-first': ['solution', 'question', 'thought']
};
return orders[approach] || orders['thought-first'];
}
getProcessingStage(element) {
const stageMap = {
'thought': 'thought_processing',
'question': 'question_processing',
'solution': 'solution_processing'
};
return stageMap[element];
}
isProcessingStage(stage) {
return ['thought_processing', 'question_processing', 'solution_processing'].includes(stage);
}
getCurrentElementIndex(currentElement, elementOrder) {
return elementOrder.indexOf(currentElement);
}
}
// Domain-based Strategy Selector
class DomainStrategySelector {
static getDomainMapping() {
return {
// Thought-first domains (analytical, principle-based)
'mathematical': 'thought-first',
'scientific': 'thought-first',
'science': 'thought-first',
'math': 'thought-first',
'analytical': 'thought-first',
'theoretical': 'thought-first',
'physics': 'thought-first',
'chemistry': 'thought-first',
'engineering': 'thought-first',
// Question-first domains (exploratory, philosophical)
'philosophical': 'question-first',
'ethics': 'question-first',
'philosophy': 'question-first',
'symbolism': 'question-first',
'metaphysics': 'question-first',
'existential': 'question-first',
'moral': 'question-first',
'social': 'question-first',
// Solution-first domains (practical, action-oriented)
'practical': 'solution-first',
'emergency': 'solution-first',
'ai': 'solution-first',
'daily-life': 'solution-first',
'creative': 'solution-first',
'business': 'solution-first',
'troubleshooting': 'solution-first',
'debugging': 'solution-first',
'implementation': 'solution-first',
'productivity': 'solution-first'
};
}
static selectOptimalStrategy(problemDescription, domainHint = null) {
// If explicit domain hint provided, use it
if (domainHint) {
const mapping = this.getDomainMapping();
const approach = mapping[domainHint.toLowerCase()];
if (approach) {
return {
strategy: 'cyclic_reasoning',
reasoningApproach: approach,
domainContext: domainHint,
rationale: `Selected ${approach} approach based on ${domainHint} domain`
};
}
}
// Analyze problem description for domain keywords
if (problemDescription) {
const text = problemDescription.toLowerCase();
const mapping = this.getDomainMapping();
// Check for domain keywords in the problem description
for (const [domain, approach] of Object.entries(mapping)) {
if (text.includes(domain) || text.includes(domain.slice(0, -1))) { // handle plurals
return {
strategy: 'cyclic_reasoning',
reasoningApproach: approach,
domainContext: domain,
rationale: `Detected ${domain} domain in problem description, using ${approach} approach`
};
}
}
// Check for approach-specific keywords
if (text.match(/\b(formula|equation|theorem|proof|calculate|derive)\b/)) {
return {
strategy: 'cyclic_reasoning',
reasoningApproach: 'thought-first',
domainContext: 'analytical',
rationale: 'Detected analytical keywords, using thought-first approach'
};
}
if (text.match(/\b(why|what if|should|ought|meaning|purpose|value)\b/)) {
return {
strategy: 'cyclic_reasoning',
reasoningApproach: 'question-first',
domainContext: 'philosophical',
rationale: 'Detected philosophical keywords, using question-first approach'
};
}
if (text.match(/\b(how to|implement|fix|solve|urgent|need|do|action)\b/)) {
return {
strategy: 'cyclic_reasoning',
reasoningApproach: 'solution-first',
domainContext: 'practical',
rationale: 'Detected practical keywords, using solution-first approach'
};
}
}
// Default fallback
return {
strategy: 'cyclic_reasoning',
reasoningApproach: 'thought-first',
domainContext: 'general',
rationale: 'No specific domain detected, defaulting to thought-first approach'
};
}
static getApproachDescription(approach) {
const descriptions = {
'thought-first': 'Analyze principles and build understanding before questioning and solving',
'question-first': 'Start with key questions to explore the problem space before developing solutions',
'solution-first': 'Begin with practical solutions, then understand why they work'
};
return descriptions[approach] || 'Unknown approach';
}
}
// Parameter Router for semantic routing
class ParameterRouter {
constructor() {
// Load semantic routing configuration
const semanticConfigPath = path.join(__dirname, 'semantic-routing-config.json');
this.semanticConfig = JSON.parse(fs.readFileSync(semanticConfigPath, 'utf8'));
}
getAvailableActions(strategy, currentStage, thoughtHistory) {
const strategySemantics = this.semanticConfig.strategySemantics[strategy];
if (!strategySemantics || !strategySemantics[currentStage]) {
return null;
}
const stageConfig = strategySemantics[currentStage];
const availableActions = { ...(stageConfig.availableActions || {}) };
// Add global actions if applicable
Object.entries(this.semanticConfig.globalActions).forEach(([actionName, actionConfig]) => {
if (actionConfig.availableFrom === "any" ||
(Array.isArray(actionConfig.availableFrom) && actionConfig.availableFrom.includes(currentStage))) {
availableActions[actionName] = {
description: actionConfig.description,
requiredInputs: actionConfig.requiredInputs,
optionalInputs: actionConfig.optionalInputs,
hints: actionConfig.hints,
isGlobal: true
};
}
});
return availableActions;
}
getRequiredParameters(strategy, currentStage, actionName) {
const actions = this.getAvailableActions(strategy, currentStage, []);
if (!actions || !actions[actionName]) {
return [];
}
return actions[actionName].requiredInputs || [];
}
getOptionalParameters(strategy, currentStage, actionName) {
const actions = this.getAvailableActions(strategy, currentStage, []);
if (!actions || !actions[actionName]) {
return [];
}
return actions[actionName].optionalInputs || [];
}
getParameterHints(strategy, currentStage, actionName) {
const actions = this.getAvailableActions(strategy, currentStage, []);
if (!actions || !actions[actionName]) {
return {};
}
return actions[actionName].hints || {};
}
getStageDescription(strategy, currentStage) {
const strategySemantics = this.semanticConfig.strategySemantics[strategy];
if (!strategySemantics || !strategySemantics[currentStage]) {
return "No description available";
}
return strategySemantics[currentStage].description;
}
canSwitchStrategy(strategy, currentStage) {
const strategySemantics = this.semanticConfig.strategySemantics[strategy];
if (!strategySemantics || !strategySemantics[currentStage]) {
return false;
}
return strategySemantics[currentStage].canSwitchStrategy || false;
}
determineActionTaken(strategy, currentStage, providedParams) {
const availableActions = this.getAvailableActions(strategy, currentStage, []);
// Handle null availableActions (strategy doesn't exist in config)
if (!availableActions) {
console.error(`Warning: No available actions for strategy '${strategy}' at stage '${currentStage}'`);
return null;
}
// Base parameters that are always present
const baseParams = ['thought', 'thoughtNumber', 'totalThoughts', 'nextThoughtNeeded', 'strategy'];
// Get all non-base parameters provided
const providedNonBaseParams = Object.keys(providedParams).filter(
param => !baseParams.includes(param) && providedParams[param] !== undefined
);
// Check each available action to see if its required parameters are satisfied
for (const [actionName, actionConfig] of Object.entries(availableActions)) {
const requiredInputs = actionConfig.requiredInputs || [];
const nonBaseRequired = requiredInputs.filter(param => !baseParams.includes(param));
// If action has non-base required params, check if they're all provided
if (nonBaseRequired.length > 0) {
const hasAllRequired = nonBaseRequired.every(param =>
providedParams[param] !== undefined && providedParams[param] !== null
);
if (hasAllRequired) {
return {
actionName,
nextState: actionConfig.nextState,
isGlobal: actionConfig.isGlobal || false
};
}
}
}
// If no specific action matches, find the default action (one with only base params required)
for (const [actionName, actionConfig] of Object.entries(availableActions)) {
const requiredInputs = actionConfig.requiredInputs || [];
const hasOnlyBaseParams = requiredInputs.every(param => baseParams.includes(param));
if (hasOnlyBaseParams && !actionConfig.isGlobal) {
return {
actionName,
nextState: actionConfig.nextState,
isGlobal: false
};
}
}
return null;
}
buildSemanticResponse(strategy, currentStage, thoughtHistory, sessionId) {
console.error("DEBUG: buildSemanticResponse called with:", {
strategy,
currentStage,
thoughtHistoryType: typeof thoughtHistory,
thoughtHistoryIsArray: Array.isArray(thoughtHistory),
sessionId
});
// Ensure thoughtHistory is always an array
const history = thoughtHistory || [];
const availableActions = this.getAvailableActions(strategy, currentStage, history);
const stageDescription = this.getStageDescription(strategy, currentStage);
return {
currentState: currentStage,
stateDescription: stageDescription,
sessionToken: sessionId,
availableActions: availableActions,
canSwitchStrategy: this.canSwitchStrategy(strategy, currentStage),
thoughtHistoryLength: history.length
};
}
}
// MCP Client Manager for connecting to other MCP servers
class MCPClientManager {
constructor() {
this.clients = new Map();
this.availableServers = new Map();
}
async connectToServer(serverName, command, args = []) {
if (this.clients.has(serverName)) {
return this.clients.get(serverName);
}
try {
const client = new Client({
name: `thinking-server-client-${serverName}`,
version: "1.0.0"
}, {
capabilities: {}
});
const transport = new StdioClientTransport({
command: command,
args: args
});
await client.connect(transport);
this.clients.set(serverName, client);
this.availableServers.set(serverName, { command, args });
console.error(chalk.green(`๐ Connected to MCP server: ${serverName}`));
return client;
} catch (error) {
console.error(chalk.red(`โ Failed to connect to ${serverName}: ${error.message}`));
throw error;
}
}
async disconnectFromServer(serverName) {
const client = this.clients.get(serverName);
if (client) {
await client.close();
this.clients.delete(serverName);
this.availableServers.delete(serverName);
console.error(chalk.yellow(`๐ Disconnected from MCP server: ${serverName}`));
}
}
async executeToolCall(serverName, toolName, parameters, context = {}) {
const client = this.clients.get(serverName);
if (!client) {
throw new Error(`Not connected to server: ${serverName}`);
}
try {
console.error(chalk.cyan(`๐ง Executing ${serverName}.${toolName}`));
const result = await client.callTool({
name: toolName,
arguments: parameters || {}
});
return {
success: true,
result: result,
server: serverName,
tool: toolName,
parameters: parameters,
context: context,
timestamp: new Date().toISOString()
};
} catch (error) {
console.error(chalk.red(`โ Tool call failed: ${serverName}.${toolName} - ${error.message}`));
return {
success: false,
error: error.message,
server: serverName,
tool: toolName,
parameters: parameters,
context: context,
timestamp: new Date().toISOString()
};
}
}
async listAvailableTools(serverName) {
const client = this.clients.get(serverName);
if (!client) {
throw new Error(`Not connected to server: ${serverName}`);
}
try {
const tools = await client.listTools();
return tools.tools || [];
} catch (error) {
console.error(chalk.red(`โ Failed to list tools for ${serverName}: ${error.message}`));
return [];
}
}
getConnectedServers() {
return Array.from(this.clients.keys());
}
isConnected(serverName) {
return this.clients.has(serverName);
}
}
// Sequential Thinking implementation
class SequentialThinkingServer {
thoughtHistory = [];
branches = {};
sessionId = null;
storage = null;
stageManager = null;
parameterRouter = null;
strategy = null;
isInitialized = false;
// Session metadata for knowledge building
sessionPurpose = null;
qualityRating = null;
sessionStartTime = null;
// MCP client integration
mcpClientManager = null;
pendingActions = [];
actionResults = [];
// Dual numbering system
absoluteThoughtNumber = 0;
sequenceThoughtNumber = 0;
constructor(storage) {
this.storage = storage;
this.parameterRouter = new ParameterRouter();
this.mcpClientManager = new MCPClientManager();
console.error(chalk.blue("Sequential Thinking Server initialized - waiting for strategy selection"));
}
resetSession(strategy) {
if (!strategy) {
throw new Error("Strategy must be specified when resetting session");
}
this.thoughtHistory = [];
this.branches = {};
this.sessionId = this.generateSessionId(strategy);
this.strategy = strategy;
this.stageManager = new StageManager(strategy);
this.isInitialized = true;
// Reset session metadata
this.sessionPurpose = null;
this.qualityRating = null;
this.sessionStartTime = new Date().toISOString();
// Reset dual numbering system
this.absoluteThoughtNumber = 0;
this.sequenceThoughtNumber = 0;
// Reset action tracking
this.pendingActions = [];
this.actionResults = [];
console.error(`New thinking session started with ID: ${this.sessionId} using strategy: ${strategy}`);
}
generateSessionId(strategy) {
const timestamp = new Date().toISOString()
.replace(/[-:]/g, '')
.replace('T', '-')
.replace(/\..+/, '');
return `${strategy}-session-${timestamp}`;
}
// Action planning and execution methods
async connectToMCPServer(serverName, command, args = []) {
try {
await this.mcpClientManager.connectToServer(serverName, command, args);
return {
success: true,
message: `Connected to ${serverName}`,
connectedServers: this.mcpClientManager.getConnectedServers()
};
} catch (error) {
return {
success: false,
error: error.message,
connectedServers: this.mcpClientManager.getConnectedServers()
};
}
}
async executeAction(action) {
const { server, tool, parameters, context } = action;
if (!this.mcpClientManager.isConnected(server)) {
throw new Error(`Not connected to MCP server: ${server}`);
}
console.error(chalk.blue(`๐ฏ Executing planned action: ${server}.${tool}`));
const result = await this.mcpClientManager.executeToolCall(server, tool, parameters, {
...context,
sessionId: this.sessionId,
strategy: this.strategy,
thoughtNumber: this.absoluteThoughtNumber
});
this.actionResults.push(result);
console.error(chalk.green(`โ
Action completed: ${server}.${tool}`));
return result;
}
async executePendingActions() {
if (this.pendingActions.length === 0) {
return [];
}
console.error(chalk.cyan(`โก Executing ${this.pendingActions.length} pending actions`));
const results = [];
for (const action of this.pendingActions) {
try {
const result = await this.executeAction(action);
results.push(result);
} catch (error) {
console.error(chalk.red(`โ Action failed: ${error.message}`));
results.push({
success: false,
error: error.message,
action: action
});
}
}
this.pendingActions = []; // Clear pending actions after execution
return results;
}
planAction(server, tool, parameters, expectedInfo) {
const action = {
server,
tool,
parameters,
expectedInfo,
plannedAt: new Date().toISOString(),
context: {
sessionId: this.sessionId,
strategy: this.strategy,
currentStage: this.stageManager.getCurrentStage()
}
};
this.pendingActions.push(action);
console.error(chalk.yellow(`๐ Planned action: ${server}.${tool} - ${expectedInfo}`));
return action;
}
inferMissingParameters(input) {
const inferred = { ...input };
const autoCompleted = {};
// Auto-complete strategy from context or parameter hints
if (!inferred.strategy) {
// Priority 1: Use active session strategy
if (this.isInitialized && this.strategy) {
inferred.strategy = this.strategy;
autoCompleted.strategy = this.strategy;
}
// Priority 2: Infer from strategy-specific parameters
else if (inferred.action && ['thought', 'action', 'observation'].includes(inferred.action)) {
inferred.strategy = 'react';
autoCompleted.strategy = 'react (from action parameter)';
}
else if (inferred.approaches || inferred.approachId || inferred.evaluationScore) {
inferred.strategy = 'tree_of_thoughts';
autoCompleted.strategy = 'tree_of_thoughts (from ToT parameters)';
}
else if (inferred.objectives || inferred.tradeOffMatrix || inferred.equilibriumReached) {
inferred.strategy = 'trilemma';
autoCompleted.strategy = 'trilemma (from objectives)';
}
else if (inferred.reasoningApproach || inferred.currentElement || inferred.cycleNumber) {
inferred.strategy = 'cyclic_reasoning';
autoCompleted.strategy = 'cyclic_reasoning (from cycle parameters)';
}
else if (inferred.subQuestion || inferred.subQuestionAnswer) {
inferred.strategy = 'self_ask';
autoCompleted.strategy = 'self_ask (from sub-questions)';
}
else if (inferred.generalPrinciple) {
inferred.strategy = 'step_back';
autoCompleted.strategy = 'step_back (from principle)';
}
else if (inferred.reasoningPathId || inferred.pathAnswers) {
inferred.strategy = 'self_consistency';
autoCompleted.strategy = 'self_consistency (from paths)';
}
else if (inferred.planningPhase || inferred.toolCalls) {
inferred.strategy = 'rewoo';
autoCompleted.strategy = 'rewoo (from planning)';
}
else if (inferred.stateVariables) {
inferred.strategy = 'scratchpad';
autoCompleted.strategy = 'scratchpad (from state)';
}
// Priority 3: Analyze thought content
else if (inferred.thought) {
const thoughtLower = inferred.thought.toLowerCase();
if (thoughtLower.includes('observe') || thoughtLower.includes('action:')) {
inferred.strategy = 'react';
autoCompleted.strategy = 'react (from thought keywords)';
} else if (thoughtLower.includes('approach') || thoughtLower.includes('branch')) {
inferred.strategy = 'tree_of_thoughts';
autoCompleted.strategy = 'tree_of_thoughts (from thought keywords)';
} else if (thoughtLower.includes('step back') || thoughtLower.includes('principle')) {
inferred.strategy = 'step_back';
autoCompleted.strategy = 'step_back (from thought keywords)';
} else if (thoughtLower.includes('trade-off') || thoughtLower.includes('balance')) {
inferred.strategy = 'trilemma';
autoCompleted.strategy = 'trilemma (from thought keywords)';
} else {
inferred.strategy = 'chain_of_thought';
autoCompleted.strategy = 'chain_of_thought (default)';
}
}
else {
inferred.strategy = 'chain_of_thought';
autoCompleted.strategy = 'chain_of_thought (default)';
}
}
// Auto-complete thoughtNumber from session history
if (inferred.thoughtNumber === undefined || inferred.thoughtNumber === null) {
if (this.thoughtHistory && this.thoughtHistory.length > 0) {
const lastThought = this.thoughtHistory[this.thoughtHistory.length - 1];
inferred.thoughtNumber = (lastThought.thoughtNumber || 0) + 1;
autoCompleted.thoughtNumber = `${inferred.thoughtNumber} (continued from ${lastThought.thoughtNumber})`;
} else {
inferred.thoughtNumber = 1;
autoCompleted.thoughtNumber = '1 (session start)';
}
}
// Auto-complete totalThoughts based on context
if (inferred.totalThoughts === undefined || inferred.totalThoughts === null) {
if (inferred.nextThoughtNeeded === false) {
inferred.totalThoughts = inferred.thoughtNumber;
autoCompleted.totalThoughts = `${inferred.totalThoughts} (marked complete)`;
} else {
// Strategy-specific estimates for typical completion
const strategyEstimates = {
linear: 3,
chain_of_thought: 5,
react: 7,
rewoo: 6,
scratchpad: 5,
self_ask: 8,
self_consistency: 6,
step_back: 4,
tree_of_thoughts: 10,
trilemma: 8,
cyclic_reasoning: 9
};
const estimate = strategyEstimates[inferred.strategy] || 5;
inferred.totalThoughts = Math.max(inferred.thoughtNumber + 1, estimate);
autoCompleted.totalThoughts = `${inferred.totalThoughts} (estimated for ${inferred.strategy})`;
}
}
// Auto-complete nextThoughtNeeded from context
if (typeof inferred.nextThoughtNeeded !== 'boolean') {
if (inferred.finalAnswer || inferred.equilibriumReached || inferred.hypothesis) {
inferred.nextThoughtNeeded = false;
autoCompleted.nextThoughtNeeded = 'false (solution reached)';
} else if (inferred.thoughtNumber >= inferred.totalThoughts) {
inferred.nextThoughtNeeded = false;
autoCompleted.nextThoughtNeeded = 'false (reached total)';
} else {
inferred.nextThoughtNeeded = true;
autoCompleted.nextThoughtNeeded = 'true (continuing)';
}
}
// Provide default thought if missing
if (!inferred.thought) {
inferred.thought = 'Continuing analysis...';
autoCompleted.thought = 'default placeholder';
}
// Store auto-completion hints for response
if (Object.keys(autoCompleted).length > 0) {
inferred._autoCompletionHints = autoCompleted;
console.error(chalk.cyan('๐ฎ Parameters auto-completed:'));
Object.entries(autoCompleted).forEach(([key, value]) => {
console.error(chalk.cyan(` โข ${key}: ${value}`));
});
}
return inferred;
}
validateThoughtData(input) {
// Apply intelligent parameter inference to auto-complete missing values
const data = this.inferMissingParameters(input);
// Check if this is a strategy selection or reset request
if (data.strategy && (!data.thoughtNumber || data.thoughtNumber === 1)) {
// Capture sessionPurpose before reset
const capturedSessionPurpose = data.sessionPurpose;
this.resetSession(data.strategy);
// Restore sessionPurpose after reset
if (capturedSessionPurpose) {
this.sessionPurpose = capturedSessionPurpose;
}
// Build semantic response for initial state
const semanticResponse = this.parameterRouter.buildSemanticResponse(
this.strategy,
this.stageManager.getCurrentStage(),
this.thoughtHistory,
this.sessionId
);
return {
...semanticResponse,
thought: data.thought || '',
thoughtNumber: 1,
totalThoughts: data.totalThoughts || 1,
nextThoughtNeeded: true
};
}
// Check if a strategy has been selected
if (!this.isInitialized) {
return {
error: "No strategy selected. Please select a strategy first.",
availableStrategies: Object.keys(strategyConfig.strategyStages),
status: 'waiting_for_strategy',
nextThoughtNeeded: true
};
}
// Validate required parameters for all strategies (now with self-healing applied)
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');
}
// Determine action taken based on provided parameters
const action = this.parameterRouter.determineActionTaken(
this.strategy,
this.stageManager.getCurrentStage(),
data
);
// Handle automatic stage transitions
if (action && action.nextState && !action.isGlobal) {
// Transition to next state if action determines it
if (this.stageManager.canTransitionTo(action.nextState)) {
this.stageManager.transitionTo(action.nextState);
console.error(chalk.cyan(`๐ Transitioned to: ${action.nextState} (via ${action.actionName})`));
}
} else if (data.currentStage && data.currentStage !== this.stageManager.getCurrentStage()) {
// Manual stage transition (backward compatibility)
if (!this.stageManager.canTransitionTo(data.currentStage)) {
throw new Error(`Invalid stage transition from ${this.stageManager.getCurrentStage()} to ${data.currentStage}`);
}
this.stageManager.transitionTo(data.currentStage);
} else if (this.strategy === 'cyclic_reasoning') {
// Handle cyclic reasoning automatic transitions
const nextStage = this.stageManager.determineNextCyclicStage(data);
if (nextStage && this.stageManager.canTransitionTo(nextStage)) {
this.stageManager.transitionTo(nextStage);
console.error(chalk.cyan(`๐ Cyclic transition to: ${nextStage}`));
}
}
// Handle strategy switching
if (action && action.actionName === 'switch_strategy' && data.strategy !== this.strategy) {
const preserveHistory = data.preserveHistory || false;
const oldHistory = preserveHistory ? [...this.thoughtHistory] : [];
this.resetSession(data.strategy);
if (preserveHistory) {
this.thoughtHistory = oldHistory;
}
console.error(chalk.magenta(`๐ Switched strategy to: ${data.strategy}`));
}
// Handle action planning - if plannedActions are provided, add them to pending actions
if (data.plannedActions && Array.isArray(data.plannedActions)) {
for (const actionPlan of data.plannedActions) {
const { server, tool, parameters, expectedInfo } = actionPlan;
if (server && tool) {
this.planAction(server, tool, parameters, expectedInfo || `Execute ${server}.${tool}`);
}
}
}
// Handle action results - if actionResults are provided, integrate them
if (data.actionResults && Array.isArray(data.actionResults)) {
this.actionResults.push(...data.actionResults);
console.error(chalk.green(`๐ฅ Integrated ${data.actionResults.length} action results`));
}
// Return validated data
return {
...data,
strategy: this.strategy,
currentStage: this.stageManager.getCurrentStage(),
pendingActions: this.pendingActions.length,
actionResults: this.actionResults
};
}
formatThought(thoughtData) {
const {
thoughtNumber,
totalThoughts,
thought,
isRevision,
revisesThought,
branchFromThought,
branchId,
strategy,
currentStage,
nextThoughtNeeded,
absoluteNumber,
sequenceNumber
} = thoughtData;
let prefix = '';
let context = '';
if (isRevision) {
prefix = chalk.yellow('๐ Revision');
context = ` (revising thought ${revisesThought})`;
}
else if (branchFromThought) {
prefix = chalk.green('๐ฟ Branch');
context = ` (from thought ${branchFromThought}, ID: ${branchId})`;
}
else {
prefix = chalk.blue('๐ญ Thought');
context = '';
}
const strategyInfo = chalk.magenta(`[${strategy}]`);
const stageInfo = chalk.cyan(`[Stage: ${currentStage}]`);
const dualNumbering = absoluteNumber ? chalk.gray(`[A${absoluteNumber}|S${sequenceNumber}]`) : '';
const header = `${prefix} ${thoughtNumber}/${totalThoughts}${context} ${dualNumbering} ${strategyInfo} ${stageInfo}`;
const border = 'โ'.repeat(Math.max(header.length, (thought || '').length) + 4);
// Add continuation hint if more thoughts are needed
let output = `
โ${border}โ
โ ${header} โ
โ${border}โค
โ ${(thought || '').padEnd(border.length - 2)} โ`;
if (nextThoughtNeeded) {
const continuationHint = "Continue with your next thought step without stopping";
const hintBorder = 'โ'.repeat(Math.max(continuationHint.length + 4, border.length));
output += `
โ${hintBorder}โค
โ ${chalk.green(continuationHint).padEnd(hintBorder.length - 2)} โ`;
}
output += `
โ${border}โ`;
return output;
}
async processThought(input) {
try {
console.error("DEBUG: processThought called with:", JSON.stringify(input, null, 2));
const validatedInput = this.validateThoughtData(input);
// If we're waiting for strategy selection, return early
if (validatedInput.status === 'waiting_for_strategy') {
return validatedInput;
}
// Log strategy start
if (this.stageManager.isFirstStage() && validatedInput.thoughtNumber === 1) {
console.error(chalk.green(`๐ฏ Starting ${this.strategy} strategy`));
console.error(chalk.cyan(`๐ Stage: ${this.stageManager.getCurrentStage()}`));
}
// Normal thought processing
if (validatedInput.thoughtNumber > validatedInput.totalThoughts) {
validatedInput.totalThoughts = validatedInput.thoughtNumber;
}
// Update dual numbering system
this.absoluteThoughtNumber++;
// Handle branching - reset sequence number for new branches
if (validatedInput.branchFromThought && validatedInput.branchId) {
// Reset sequence numbering for new branch
this.sequenceThoughtNumber = 1;
console.error(chalk.green(`๐ฟ New branch ${validatedInput.branchId} started from thought A${validatedInput.branchFromThought}`));
} else {
this.sequenceThoughtNumber++;
}
// Add dual numbering to validated input
validatedInput.absoluteNumber = this.absoluteThoughtNumber;
validatedInput.sequenceNumber = this.sequenceThoughtNumber;
this.thoughtHistory.push(validatedInput);
if (validatedInput.branchFromThought && validatedInput.branchId) {
if (!this.branches[validatedInput.branchId]) {
this.branches[validatedInput.branchId] = [];
}
this.branches[validatedInput.branchId].push(validatedInput);
}
const formattedThought = this.formatThought({
...validatedInput,
strategy: this.strategy,
currentStage: this.stageManager.getCurrentStage()
});
console.error(formattedThought);
// Handle session metadata updates
if (validatedInput.sessionPurpose) {
this.sessionPurpose = validatedInput.sessionPurpose;
}
if (validatedInput.qualityRating) {
this.qualityRating = validatedInput.qualityRating;
}
// Save session incrementally after each thought (for resilience)
if (this.storage) {
try {
const savedPath = await this.storage.saveSession(
this.sessionId,
this.thoughtHistory,
this.branches,
this.sessionPurpose,
this.qualityRating,
this.sessionStartTime
);
// Only log completion message for final thought
if (!validatedInput.nextThoughtNeeded) {
console.error(chalk.green(`โ
Thinking session completed and saved to: ${savedPath}`));
}
} catch (saveError) {
console.error(chalk.red(`โ Error saving thinking session: ${saveError.message}`));
}
}
// Build semantic routing response
const semanticResponse = this.parameterRouter.buildSemanticResponse(
this.strategy,
this.stageManager.getCurrentStage(),
this.thoughtHistory,
this.sessionId
);
// Build response with auto-completion hints if present
const response = {
...semanticResponse,
thoughtNumber: validatedInput.thoughtNumber,
totalThoughts: validatedInput.totalThoughts,
nextThoughtNeeded: validatedInput.nextThoughtNeeded,
sessionSaved: !validatedInput.nextThoughtNeeded
};
// Include auto-completion hints to help agents
if (validatedInput._autoCompletionHints) {
response.autoCompletionNote = "๐ฎ Parameters auto-completed from context. Include these in your next call:";
response.autoCompleted = validatedInput._autoCompletionHints;
}
return response;
}
catch (error) {
console.error("DEBUG: Error in processThought:", error);
console.error("DEBUG: Error stack:", error.stack);
return {
error: error instanceof Error ? error.message : String(error),
status: 'failed'
};
}
}
}
// Detailed documentation for the Sequential Thinking tool
const SEQUENTIAL_THINKING_DOCUMENTATION = `# SequentialThinking Plus Tool
A modular tool for dynamic and reflective problem-solving through structured thoughts using various reasoning strategies.
## Overview
This tool helps analyze problems through flexible thinking processes that can adapt and evolve based on the selected strategy. Each strategy offers a different approach to problem-solving, with its own strengths and specialized use cases. The underlying flow engine supports unfixed step numbers and branching approaches, allowing for truly adaptive reasoning.
**NEW: Intelligent Parameter Auto-Completion** ๐ฎ
The tool now automatically infers missing parameters from context to ensure continuity of thinking. If you forget parameters like 'strategy' or 'thoughtNumber', they will be intelligently inferred based on:
- Active session context
- Strategy-specific parameters you provide
- Keywords in your thought content
- Previous thought history
When parameters are auto-completed, you'll receive helpful hints in the response showing what was inferred, so you can adjust in subsequent calls if needed.
**IMPORTANT**: While auto-completion helps reduce errors, explicitly providing parameters when known will give you more control over the thinking process.
**KEY FEATURE**: Between any thinking steps, you can call other tools for research, file access, calculations, or actions. The thinking strategy will pause while you gather information, then resume where you left off.
**STRATEGIC PLANNING**: Your thoughts can include plans for future tool calls (e.g., "Step 3: search files for config", "Next: run calculation tool"). You can outline your tool usage strategy within your thinking, then execute those planned calls before advancing to the next step.
## Available Strategies
1. **Linear** - Exploratory approach with manual progression control. You choose when to advance through each thinking stage, and can call other tools between any steps for research, file access, or actions. Perfect for deliberative problem-solving where you need to gather information as you think.
2. **Chain of Thought** - A linear approach that breaks down problems into sequential reasoning steps.
3. **ReAct** - Combines reasoning with actions to gather information from external tools. Supports cyclic flows for multiple action-observation cycles.
4. **ReWOO** - Separates planning, working, and solving phases with parallel tool execution.
5. **Scratchpad** - Uses iterative calculations with explicit state tracking. Supports repeating calculation steps as needed.
6. **Self-Ask** - Breaks down problems into sub-questions that are answered sequentially. Supports asking multiple sub-questions as needed.
7. **Self-Consistency** - Explores multiple reasoning paths to find the most consistent answer.
8. **Step-Back** - Abstracts the problem to identify general principles before solving.
9. **Tree of Thoughts** - Explores multiple solution paths and evaluates their promise. Supports branching and path selection.
## Flow Engine Architecture
The Sequential Thinking tool is powered by a flexible flow engine that supports:
1. **Unfixed Step Numbers**:
- Cyclic stage transitions allow repeating steps as needed
- Dynamic thought count adjustment during the thinking process
- Strategy-specific iteration points for different reasoning approaches
2. **Branching Approaches**:
- Explicit branch tracking for exploring alternative paths
- Strategy-specific branching mechanisms (especially in Tree of Thoughts)
- Branch creation, development, and selection capabilities
3. **Strategy-Specific Flows**:
- Each strategy has its own defined flow pattern
- Customized stage transitions for different reasoning approaches
- Specialized stages for different problem-solving techniques
## How to Use
### Getting Started
1. **Choose your strategy first!** Don't default to linear. Pick from:
- linear, chain_of_thought, react, rewoo, scratchpad,
- self_ask, self_consistency, step_back, tree_of_thoughts, or trilemma
2. Start your first thought with your chosen strategy:
- Set the 'strategy' parameter to your selected strategy
- Set 'thoughtNumber' to 1 for the first thought
2. The tool will respond with:
- An introduction to the selected strategy
- The current stage in the thinking process
- Required parameters for the next stage
- A prompt for the next step
3. Follow the guided process through each stage of the selected strategy:
- Provide the required parameters for each stage
- The tool will guide you through the appropriate sequence of stages
- Each response includes the next stage prompt and required parameters
- You can cycle back to previous stages when needed based on the strategy's flow pattern
### Core Parameters (All Strategies)
- **strategy**: The thinking strategy being employed
- **thought**: Your current thinking step
- **thoughtNumber**: Current thought number
- **totalThoughts**: Estimated total thoughts needed (can be adjusted dynamically)
- **nextThoughtNeeded**: Whether another thought step is needed
- **currentStage**: Current stage in the thinking process flow
- **needsMoreThoughts**: Indicates if more thoughts are needed than initially estimated
### Strategy-Specific Parameters
Each strategy may require additional parameters specific to its approach. The tool will guide you on which parameters are needed at each stage.
#### Branching Parameters
- **branchFromThought**: The thought number from which to branch
- **branchId**: Unique identifier for the branch
- **isRevision**: Whether this thought revises a previous one
- **revisesThought**: Which thought is being revised
#### Strategy-Specific Flow Parameters
- **ReAct**: action, observation
- **ReWOO**: planningPhase, toolCalls
- **Scratchpad**: stateVariables
- **Self-Ask**: subQuestion, subQuestionAnswer, subQuestionNumber
- **Self-Consistency**: reasoningPathId, pathAnswers
- **Step-Back**: generalPrinciple
- **Tree of Thoughts**: approachId, approaches, evaluationScore
### Wizard Functionality
The tool uses semantic routing to guide you through the thinking process:
1. It confirms your strategy selection
2. It informs you which parameters you need to provide
3. It guides you through the remaining steps
4. It prompts you with specific questions for each stage
5. You can cancel the flow and start over at any time
## When to Use This Tool
- Breaking down complex problems into steps
- Planning and design with room for revision
- Analysis that might need course correction
- Problems where the full scope might not be clear initially
- Problems that require a multi-step solution
- Tasks that need to maintain context over multiple steps
- Situations where irrelevant information needs to be filtered out
- Problems that benefit from exploring multiple approaches
- Reasoning that requires iterative refinement
## Strategy Selection Guide (Quick Reference)
### Choose Your Strategy Based on Problem Type:
| Problem Type | Best Strategy | Why Use It |
|-------------|---------------|------------|
| Exploratory/deliberative thinking | **linear** | Manual control, call other tools between steps, research as you think |
| Step-by-step breakdown | **chain_of_thought** | Linear, clear sequential steps |
| Need external tools/info | **react** | Combines thinking with tool actions |
| Multiple tools upfront | **rewoo** | Plan all tools, execute in parallel |
| Math/algorithms | **scratchpad** | Track variables, show calculations |
| Complex questions | **self_ask** | Break into sub-questions |
| Verify correctness | **self_consistency** | Multiple paths โ consensus |
| Abstract โ specific | **step_back** | Find principles first, then apply |
| Multiple approaches | **tree_of_thoughts** | Explore & evaluate different paths |
| Three-way trade-offs | **trilemma** | Balance competing objectives through satisficing |
## Session Management
Each thinking session is stored with a unique ID that includes the strategy name. Sessions are never deleted, allowing you to reference past thinking processes.
## Integration with Other Tools
Sequential Thinking serves as an excellent force multiplier when combined with other tools:
- **Research Tools**: Pair with web crawlers and search tools to gather information that informs your structured thinking process. For example, use ReAct strategy with search tools to dynamically explore information as needed.
- **Memory Tools**: Create persistent records of your thinking sessions that can be referenced in future problem-solving. The session storage feature automatically preserves your thought process.
- **Data Analysis Tools**: Combine with data processing tools to analyze complex datasets through structured thinking steps.
- **Visualization Tools**: Use the output of your thinking process to generate diagrams, charts, or other visual representations.
- **Decision Support Systems**: Feed the results of your sequential thinking into decision matrices or other frameworks.
Remember to read the documentation resource first to select the most appropriate strategy, then leverage these tool combinations to maximize effectiveness.`;
class ThinkingSessionStorage {
constructor(storagePath) {
this.storagePath = storagePath;
}
calculateAutomaticMetrics(thoughtHistory, sessionStartTime, hasActions) {
const sessionEndTime = new Date().toISOString();
const durationMs = new Date(sessionEndTime) - new Date(sessionStartTime);
const durationMinutes = Math.round(durationMs / (1000 * 60));
// Calculate iteration ratio (revisions / total thoughts)
const revisionCount = thoughtHistory.filter(t => t.isRevision).length;
const iterationRatio = thoughtHistory.length > 0 ? revisionCount / thoughtHistory.length : 0;
return {
duration: durationMinutes,
iterationRatio: Math.round(iterationRatio * 100) / 100, // Round to 2 decimals
toolIntegration: hasActions
};
}
async saveSession(sessionId, thoughtHistory, branches, sessionPurpose = null, qualityRating = null, sessionStartTime = null) {
const sessionDir = path.join(this.storagePath, sessionId);
await fs.ensureDir(sessionDir);
// Calculate automatic metrics
const hasActions = thoughtHistory.some(t => t.plannedActions || t.actionResults);
const automaticMetrics = sessionStartTime ?
this.calculateAutomaticMetrics(thoughtHistory, sessionStartTime, hasActions) :
null;
const sessionData = {
id: sessionId,
timestamp: new Date().toISOString(),
strategy: sessionId.split('-')[0], // Extract strategy from sessionId
sessionPurpose,
qualityRating,
automaticMetrics,
thoughtHistory,
branches
};
const filePath = path.join(sessionDir, 'session.json');
await fs.writeJson(filePath, sessionData, { spaces: 2 });
return filePath;
}
// Extract key topics and terms from thought content
extractTopics(thoughtHistory) {
const allText = thoughtHistory.map(t => t.thought).join(' ');
// Simple topic extraction - remove common words and get meaningful terms
const stopWords = new Set(['the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'from', 'up', 'about', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'between', 'among', 'this', 'that', 'these', 'those', 'i', 'you', 'he', 'she', 'it', 'we', 'they', 'me', 'him', 'her', 'us', 'them', 'my', 'your', 'his', 'her', 'its', 'our', 'their', 'am', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should', 'may', 'might', 'must', 'can', 'shall']);
const words = allText.toLowerCase()
.replace(/[^\w\s]/g, ' ')
.split(/\s+/)
.filter(word => word.length > 2 && !stopWords.has(word));
// Count word frequency
const wordCounts = {};
words.forEach(word => {
wordCounts[word] = (wordCounts[word] || 0) + 1;
});
// Get top terms
const topTerms = Object.entries(wordCounts)
.sort(([,a], [,b]) => b - a)
.slice(0, 8)
.map(([word, count]) => ({ term: word, frequency: count }));
return topTerms;
}
// Calculate session complexity based on branching, actions, and thought depth
calculateComplexity(thoughtHistory, branches) {
const thoughtComplexity = thoughtHistory.length;
const branchComplexity = Object.keys(branches).length * 2;
const actionComplexity = thoughtHistory.filter(t => t.plannedActions || t.actionResults).length;
const totalComplexity = thoughtComplexity + branchComplexity + actionComplexity;
if (totalComplexity < 5) return 'simple';
if (totalComplexity < 15) return 'moderate';
if (totalComplexity < 30) return 'complex';
return 'very-complex';
}
// Determine completion status
getCompletionStatus(thoughtHistory) {
if (thoughtHistory.length === 0) return 'empty';
const lastThought = thoughtHistory[thoughtHistory.length - 1];
const hasConclusion = lastThought.finalAnswer || !lastThought.nextThoughtNeeded;
const hasSignificantProgress = thoughtHistory.length >= 3;
if (hasConclusion && hasSignificantProgress) return 'completed';
if (hasSignificantProgress) return 'in-progress';
return 'started';
}
// Search sessions by content with term scoring
async searchSessions(query, options = {}) {
const {
limit = 10,
offset = 0,
minScore = 0.1,
strategy = null,
completionStatus = null
} = options;
const sessions = await this.getAllSessions();
const queryTerms = query.toLowerCase().split(/\s+/).filter(term => term.length > 2);
const scoredSessions = sessions.map(session => {
const sessionText = session.thoughtHistory.map(t => t.thought).join(' ').toLowerCase();
// Calculate relevance score
let score = 0;
queryTerms.forEach(term => {
const regex = new RegExp(term, 'gi');
const matches = sessionText.match(regex) || [];
score += matches.length;
});
// Normalize by session length
const normalizedScore = score / Math.max(sessionText.length / 1000, 1);
return {
...session,
searchScore: normalizedScore,
matchedTerms: queryTerms.filter(term => sessionText.includes(term))
};
});
// Filter and sort by relevance
let filteredSessions = scoredSessions
.filter(s => s.searchScore >= minScore)
.filter(s => !strategy || s.strategy === strategy)
.filter(s => !completionStatus || s.completion === completionStatus)
.sort((a, b) => b.searchScore - a.searchScore);
const total = filteredSessions.length;
filteredSessions = filteredSessions.slice(offset, offset + limit);
return {
sessions: filteredSessions,
total,
offset,
limit,
hasMore: offset + limit < total
};
}
async listSessions(options = {}) {
const {
limit = 10,
offset = 0,
strategy = null,
includeTopics = false,
sortBy = 'timestamp' // timestamp, thoughtCount, complexity
} = options;
await fs.ensureDir(this.storagePath);
const dirs = await fs.readdir(this.storagePath);
const sessions = [];
for (const dir of dirs) {
const sessionPath = path.join(this.storagePath, dir, 'session.json');
if (await fs.pathExists(sessionPath)) {
try {
const sessionData = await fs.readJson(sessionPath);
const sessionInfo = {
id: sessionData.id,
timestamp: sessionData.timestamp,
strategy: sessionData.strategy || "unknown",
sessionPurpose: sessionData.sessionPurpose,
qualityRating: sessionData.qualityRating,
automaticMetrics: sessionData.automaticMetrics,
thoughtCount: sessionData.thoughtHistory.length,
branchCount: Object.keys(sessionData.branches).length,
completion: this.getCompletionStatus(sessionData.thoughtHistory),
complexity: this.calculateComplexity(sessionData.thoughtHistory, sessionData.branches),
age: `${Math.round((Date.now() - new Date(sessionData.timestamp)) / (1000 * 60))} minutes ago`
};
if (includeTopics) {
sessionInfo.topics = this.extractTopics(sessionData.thoughtHistory);
}
sessions.push(sessionInfo);
} catch (error) {
console.error(`Error reading session ${dir}:`, error);
}
}
}
// Filter by strategy if specified
const filteredSessions = strategy ?
sessions.filter(s => s.strategy === strategy) :
sessions;
// Sort sessions
filteredSessions.sort((a, b) => {
switch (sortBy) {
case 'thoughtCount':
return b.thoughtCount - a.thoughtCount;
case 'complexity':
const complexityOrder = { 'simple': 1, 'moderate': 2, 'complex': 3, 'very-complex': 4 };
return complexityOrder[b.complexity] - complexityOrder[a.complexity];
case 'quality':
// Calculate average quality score
const getAvgQuality = (session) => {
if (!session.qualityRating) return 0;
const ratings = Object.values(session.qualityRating).filter(v => typeof v === 'number');
return ratings.length > 0 ? ratings.reduce((sum, val) => sum + val, 0) / ratings.length : 0;
};
return getAvgQuality(b) - getAvgQuality(a);
case 'duration':
const getDuration = (session) => session.automaticMetrics?.duration || 0;
return getDuration(a) - getDuration(b); // Shorter duration first (more efficient)
case 'timestamp':
default:
return new Date(b.timestamp) - new Date(a.timestamp);
}
});
const total = filteredSessions.length;
const paginatedSessions = filteredSessions.slice(offset, offset + limit);
return {
sessions: paginatedSessions,
total,
offset,
limit,
hasMore: offset + limit < total
};
}
async getAllSessions() {
await fs.ensureDir(this.storagePath);
const dirs = await fs.readdir(this.storagePath);
const sessions = [];
for (const dir of dirs) {
const sessionPath = path.join(this.storagePath, dir, 'session.json');
if (await fs.pathExists(sessionPath)) {
try {
const sessionData = await fs.readJson(sessionPath);
sessions.push({
...sessionData,
completion: this.getCompletionStatus(sessionData.thoughtHistory),
complexity: this.calculateComplexity(sessionData.thoughtHistory, sessionData.branches)
});
} catch (error) {
console.error(`Error reading session ${dir}:`, error);
}
}
}
return sessions;
}
async getSession(sessionId) {
const sessionPath = path.join(this.storagePath, sessionId, 'session.json');
if (await fs.pathExists(sessionPath)) {
return await fs.readJson(sessionPath);
}
return null;
}
}
// Create storage instance
const sessionStorage = new ThinkingSessionStorage(storagePath);
// Create the thinking server with storage
const thinkingServer = new SequentialThinkingServer(sessionStorage);
// Create an MCP server
const server = new McpServer({
name: "SequentialThinking Plus MCP Server",
version: "0.9.0",
vendor: "Aaron Bockelie"
});
// Define the core thinking schema (cleaned up - branching moved to think-tools)
const thinkingSchema = {
thought: z.string(),
nextThoughtNeeded: z.boolean().optional(), // Made optional for auto-completion
thoughtNumber: z.number().optional(), // Made optional for auto-completion
totalThoughts: z.number().optional(), // Made optional for auto-completion
strategy: z.string().optional(), // Made optional for auto-completion
// Session metadata for knowledge building
sessionPurpose: z.string().optional(),
qualityRating: z.object({
usefulness: z.number().min(1).max(5).optional(),
effectiveness: z.number().min(1).max(5).optional(),
clarity: z.number().min(1).max(5).optional(),
insights: z.number().min(1).max(5).optional(),
strategyFit: z.number().min(1).max(5).optional(),
efficiency: z.number().min(1).max(5).optional(),
actionability: z.number().min(1).max(5).optional(),
reflection: z.string().optional()
}).optional(),
// Strategy-specific parameters
action: z.string().optional(),
observation: z.string().optional(),
finalAnswer: z.string().optional(),
// Action integration for thinkโactโreflect workflow
plannedActions: z.array(z.object({
server: z.string(),
tool: z.string(),
parameters: z.record(z.any()).optional(),
expectedInfo: z.string().optional()
})).optional(),
actionResults: z.array(z.object({
success: z.boolean(),
result: z.any().optional(),
error: z.string().optional(),
server: z.string(),
tool: z.string(),
timestamp: z.string()
})).optional()
};
// Add the think-strategies tool with Zod schema
server.tool(
"think-strategies",
"Core thinking tool for structured problem-solving using 10 reasoning strategies. Choose your strategy (linear, chain_of_thought, react, rewoo, scratchpad, self_ask, self_consistency, step_back, tree_of_thoughts, trilemma) and engage in guided thinking with thinkโactโreflect workflows. Plan actions with 'plannedActions', integrate results with 'actionResults'. Use think-tools for utilities and think-session-manager for session persistence.",
thinkingSchema,
async (args) => {
try {
console.error("DEBUG: Tool handler called with args:", JSON.stringify(args, null, 2));
const result = await thinkingServer.processThought(args);
console.error("DEBUG: Tool handler result:", JSON.stringify(result, null, 2));
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
console.error("DEBUG: Error in tool handler:", error);
console.error("DEBUG: Error stack:", error.stack);
throw error;
}
}
);
// Add consolidated thinking tools
server.tool(
"think-tools",
"Utilities for thinking workflows: connect to MCP servers, execute actions, check status, create branches",
{
action: z.enum(["connect-server", "execute-actions", "server-status", "create-branch"]),
serverName: z.string().optional(),
command: z.string().optional(),
args: z.array(z.string()).optional(),
branchId: z.string().optional(),
branchFromThought: z.number().optional(),
thought: z.string().optional()
},
async (args) => {
try {
switch (args.action) {
case "connect-server":
if (!args.serverName || !args.command) {
throw new Error("serverName and command required for 'connect-server' action");
}
const connectResult = await thinkingServer.connectToMCPServer(
args.serverName,
args.command,
args.args || []
);
return {
content: [{
type: "text",
text: JSON.stringify({
action: "connect-server",
...connectResult
}, null, 2)
}]
};
case "execute-actions":
const execResults = await thinkingServer.executePendingActions();
return {
content: [{
type: "text",
text: JSON.stringify({
action: "execute-actions",
executed: execResults.length,
results: execResults
}, null, 2)
}]
};
case "server-status":
const status = {
action: "server-status",
connectedServers: thinkingServer.mcpClientManager.getConnectedServers(),
pendingActions: thinkingServer.pendingActions.length,
actionResults: thinkingServer.actionResults.length,
currentSession: thinkingServer.sessionId,
strategy: thinkingServer.strategy,
absoluteThoughtNumber: thinkingServer.absoluteThoughtNumber,
sequenceThoughtNumber: thinkingServer.sequenceThoughtNumber,
branches: Object.keys(thinkingServer.branches),
thoughtHistory: thinkingServer.thoughtHistory.length
};
return {
content: [{
type: "text",
text: JSON.stringify(status, null, 2)
}]
};
case "create-branch":
if (!args.branchId || !args.branchFromThought || !args.thought) {
throw new Error("branchId, branchFromThought, and thought required for 'create-branch' action");
}
// Find the source thought
const sourceThought = thinkingServer.thoughtHistory.find(
t => t.absoluteNumber === args.branchFromThought
);
if (!sourceThought) {
throw new Error(`No thought found with absolute number ${args.branchFromThought}`);
}
// Create branching thought data
const branchData = {
thought: args.thought,
branchFromThought: args.branchFromThought,
branchId: args.branchId,
thoughtNumber: 1,
totalThoughts: 5, // Default estimate
nextThoughtNeeded: true,
strategy: thinkingServer.strategy
};
// Process the branch creation
const branchResult = await thinkingServer.processThought(branchData);
return {
content: [{
type: "text",
text: JSON.stringify({
action: "create-branch",
message: `Branch '${args.branchId}' created from thought A${args.branchFromThought}`,
branchResult: branchResult
}, null, 2)
}]
};
default:
throw new Error(`Unknown action: ${args.action}`);
}
} catch (error) {
console.error(`ERROR: Think-tools action '${args.action}' failed:`, error);
throw error;
}
}
);
// Add unified thinking session manager tool
server.tool(
"think-session-manager",
"Manage thinking sessions: list with pagination/filtering, search by content, get detailed analysis, or resume previous sessions",
{
action: z.enum(["list", "get", "resume", "search"]),
sessionId: z.string().optional(),
limit: z.number().optional().default(10),
offset: z.number().optional().default(0),
query: z.string().optional(),
strategy: z.string().optional(),
includeTopics: z.boolean().optional().default(false),
sortBy: z.enum(["timestamp", "thoughtCount", "complexity", "quality", "duration"]).optional().default("timestamp"),
completionStatus: z.enum(["empty", "started", "in-progress", "completed"]).optional(),
minScore: z.number().optional().default(0.1)
},
async (args) => {
try {
switch (args.action) {
case "list":
const listOptions = {
limit: args.limit,
offset: args.offset,
strategy: args.strategy,
includeTopics: args.includeTopics,
sortBy: args.sortBy
};
const listResult = await sessionStorage.listSessions(listOptions);
return {
content: [{
type: "text",
text: JSON.stringify({
action: "list",
total: listResult.total,
offset: listResult.offset,
limit: listResult.limit,
hasMore: listResult.hasMore,
sessions: listResult.sessions.map(s => ({
sessionId: s.id.split('-').slice(-2).join('-'), // Extract timestamp portion
description: s.sessionPurpose || s.topics?.[0]?.term + ' analysis' || 'General thinking session',
strategy: s.strategy,
quality: s.qualityRating ? Object.values(s.qualityRating).filter(v => typeof v === 'number').reduce((sum, val, _, arr) => sum + val / arr.length, 0).toFixed(1) : null,
duration: s.automaticMetrics?.duration || null,
thoughtCount: s.thoughtCount,
completion: s.completion,
complexity: s.complexity,
age: s.age
}))
}, null, 2)
}]
};
case "get":
if (!args.sessionId) {
throw new Error("sessionId required for 'get' action");
}
// Handle both full session ID and shortened version
let fullSessionId = args.sessionId;
if (!args.sessionId.includes('session-')) {
// If it's a shortened ID, we need to find the full ID
const allSessions = await sessionStorage.listSessions({ limit: 1000 });
const matchingSession = allSessions.sessions.find(s =>
s.id.split('-').slice(-2).join('-') === args.sessionId
);
if (matchingSession) {
fullSessionId = matchingSession.id;
}
}
const session = await sessionStorage.getSession(fullSessionId);
if (!session) {
throw new Error(`Session not found: ${args.sessionId}`);
}
const sessionAnalysis = {
id: session.id,
strategy: session.strategy,
timestamp: session.timestamp,
thoughtHistory: session.thoughtHistory.map(t => ({
thoughtNumber: t.thoughtNumber,
absoluteNumber: t.absoluteNumber,
sequenceNumber: t.sequenceNumber,
thought: t.thought.substring(0, 150) + (t.thought.length > 150 ? '...' : ''),
stage: t.currentStage,
hasActions: t.plannedActions ? t.plannedActions.length > 0 : false,
hasResults: t.actionResults ? t.actionResults.length > 0 : false
})),
branches: Object.keys(session.branches),
topics: sessionStorage.extractTopics(session.thoughtHistory),
complexity: sessionStorage.calculateComplexity(session.thoughtHistory, session.branches),
completion: sessionStorage.getCompletionStatus(session.thoughtHistory),
summary: {
totalThoughts: session.thoughtHistory.length,
totalBranches: Object.keys(session.branches).length,
finalStage: session.thoughtHistory[session.thoughtHistory.length - 1]?.currentStage,
completed: !session.thoughtHistory[session.thoughtHistory.length - 1]?.nextThoughtNeeded
}
};
return {
content: [{
type: "text",
text: JSON.stringify({
action: "get",
...sessionAnalysis
}, null, 2)
}]
};
case "search":
if (!args.query) {
throw new Error("query required for 'search' action");
}
const searchOptions = {
limit: args.limit,
offset: args.offset,
minScore: args.minScore,
strategy: args.strategy,
completionStatus: args.completionStatus
};
const searchResult = await sessionStorage.searchSessions(args.query, searchOptions);
return {
content: [{
type: "text",
text: JSON.stringify({
action: "search",
query: args.query,
...searchResult,
sessions: searchResult.sessions.map(s => ({
sessionId: s.id.split('-').slice(-2).join('-'), // Extract timestamp portion for shorter ID
description: s.sessionPurpose || s.thoughtHistory[0]?.thought.substring(0, 100) + '...' || 'No description available',
strategy: s.strategy,
quality: s.qualityRating ? Object.values(s.qualityRating).filter(v => typeof v === 'number').reduce((sum, val, _, arr) => sum + val / arr.length, 0).toFixed(1) : null,
duration: s.automaticMetrics?.duration || null,
completion: s.completion,
searchScore: s.searchScore.toFixed(2),
matchedTerms: s.matchedTerms,
age: `${Math.round((Date.now() - new Date(s.timestamp)) / (1000 * 60))}m ago`
}))
}, null, 2)
}]
};
case "resume":
if (!args.sessionId) {
throw new Error("sessionId required for 'resume' action");
}
// Handle both full session ID and shortened version
let fullResumeSessionId = args.sessionId;
if (!args.sessionId.includes('session-')) {
// If it's a shortened ID, we need to find the full ID
const allSessions = await sessionStorage.listSessions({ limit: 1000 });
const matchingSession = allSessions.sessions.find(s =>
s.id.split('-').slice(-2).join('-') === args.sessionId
);
if (matchingSession) {
fullResumeSessionId = matchingSession.id;
}
}
const resumeSession = await sessionStorage.getSession(fullResumeSessionId);
if (!resumeSession) {
throw new Error(`Session not found: ${args.sessionId}`);
}
// Restore session state
thinkingServer.thoughtHistory = resumeSession.thoughtHistory;
thinkingServer.branches = resumeSession.branches;
thinkingServer.sessionId = resumeSession.id;
thinkingServer.strategy = resumeSession.strategy;
thinkingServer.stageManager = new StageManager(resumeSession.strategy);
thinkingServer.isInitialized = true;
// Reconstruct dual numbering from history
const lastThought = resumeSession.thoughtHistory[resumeSession.thoughtHistory.length - 1];
thinkingServer.absoluteThoughtNumber = lastThought?.absoluteNumber || 0;
thinkingServer.sequenceThoughtNumber = lastThought?.sequenceNumber || 0;
// Set stage to last known stage
if (lastThought?.currentStage) {
thinkingServer.stageManager.currentStage = lastThought.currentStage;
}
console.error(`๐ Resumed session: ${resumeSession.id} (${resumeSession.strategy})`);
// Build semantic response for current state
const semanticResponse = thinkingServer.parameterRouter.buildSemanticResponse(
thinkingServer.strategy,
thinkingServer.stageManager.getCurrentStage(),
thinkingServer.thoughtHistory,
thinkingServer.sessionId
);
return {
content: [{
type: "text",
text: JSON.stringify({
action: "resume",
message: `Resumed session: ${resumeSession.id}`,
strategy: resumeSession.strategy,
thoughtCount: resumeSession.thoughtHistory.length,
currentState: semanticResponse.currentState,
absoluteThoughtNumber: thinkingServer.absoluteThoughtNumber,
sequenceThoughtNumber: thinkingServer.sequenceThoughtNumber,
availableActions: semanticResponse.availableActions,
lastThought: lastThought?.thought?.substring(0, 200) + (lastThought?.thought?.length > 200 ? '...' : '')
}, null, 2)
}]
};
default:
throw new Error(`Unknown action: ${args.action}`);
}
} catch (error) {
console.error(`ERROR: Session manager action '${args.action}' failed:`, error);
throw error;
}
}
);
// Add the documentation resource
server.resource(
"documentation",
"think-strategies://documentation",
{ mimeType: "text/plain" },
async (uri) => ({
contents: [{
uri: uri.href,
text: SEQUENTIAL_THINKING_DOCUMENTATION
}]
})
);
// Add strategy configuration resource
server.resource(
"strategy-config",
"think-strategies://strategy-config",
{ mimeType: "application/json" },
async (uri) => ({
contents: [{
uri: uri.href,
text: JSON.stringify(strategyConfig, null, 2)
}]
})
);
// Start the server with stdio transport
const transport = new StdioServerTransport();
console.error("Sequential Thinking Server running on stdio");
// Connect the server to the transport
server.connect(transport).catch((error) => {
console.error("Fatal error running server:", error);
process.exit(1);
});