mirror of
https://github.com/modelcontextprotocol/servers.git
synced 2026-04-17 23:53:24 +02:00
129 lines
4.1 KiB
TypeScript
129 lines
4.1 KiB
TypeScript
import chalk from 'chalk';
|
|
|
|
export interface ThoughtData {
|
|
thought: string;
|
|
thoughtNumber: number;
|
|
totalThoughts: number;
|
|
isRevision?: boolean;
|
|
revisesThought?: number;
|
|
branchFromThought?: number;
|
|
branchId?: string;
|
|
needsMoreThoughts?: boolean;
|
|
nextThoughtNeeded: boolean;
|
|
}
|
|
|
|
export class SequentialThinkingServer {
|
|
private thoughtHistory: ThoughtData[] = [];
|
|
private branches: Record<string, ThoughtData[]> = {};
|
|
private disableThoughtLogging: boolean;
|
|
|
|
constructor() {
|
|
this.disableThoughtLogging = (process.env.DISABLE_THOUGHT_LOGGING || "").toLowerCase() === "true";
|
|
}
|
|
|
|
private validateThoughtData(input: unknown): ThoughtData {
|
|
const data = input as Record<string, unknown>;
|
|
|
|
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 as boolean | undefined,
|
|
revisesThought: data.revisesThought as number | undefined,
|
|
branchFromThought: data.branchFromThought as number | undefined,
|
|
branchId: data.branchId as string | undefined,
|
|
needsMoreThoughts: data.needsMoreThoughts as boolean | undefined,
|
|
};
|
|
}
|
|
|
|
private formatThought(thoughtData: ThoughtData): string {
|
|
const { thoughtNumber, totalThoughts, thought, isRevision, revisesThought, branchFromThought, branchId } = 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 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}┘`;
|
|
}
|
|
|
|
public processThought(input: unknown): { content: Array<{ type: string; text: string }>; isError?: boolean } {
|
|
try {
|
|
const validatedInput = this.validateThoughtData(input);
|
|
|
|
if (validatedInput.thoughtNumber > validatedInput.totalThoughts) {
|
|
validatedInput.totalThoughts = validatedInput.thoughtNumber;
|
|
}
|
|
|
|
this.thoughtHistory.push(validatedInput);
|
|
|
|
if (validatedInput.branchFromThought && validatedInput.branchId) {
|
|
if (!this.branches[validatedInput.branchId]) {
|
|
this.branches[validatedInput.branchId] = [];
|
|
}
|
|
this.branches[validatedInput.branchId].push(validatedInput);
|
|
}
|
|
|
|
if (!this.disableThoughtLogging) {
|
|
const formattedThought = this.formatThought(validatedInput);
|
|
console.error(formattedThought);
|
|
}
|
|
|
|
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) {
|
|
return {
|
|
content: [{
|
|
type: "text",
|
|
text: JSON.stringify({
|
|
error: error instanceof Error ? error.message : String(error),
|
|
status: 'failed'
|
|
}, null, 2)
|
|
}],
|
|
isError: true
|
|
};
|
|
}
|
|
}
|
|
}
|