@gotza02/sequential-thinking 2026.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # Sequential Thinking MCP Server
2
+
3
+ An MCP server implementation that provides a tool for dynamic and reflective problem-solving through a structured thinking process (Tree of Thoughts, Self-Reflexion).
4
+
5
+ ## Features
6
+
7
+ - **Sequential Analysis**: Break down complex problems into manageable linear steps.
8
+ - **Iterative Reasoning**: Think step-by-step in a structured manner, refining insights in loops.
9
+ - **Tree of Thoughts**: Generate and evaluate multiple options, exploring Conservative, Balanced, and Aggressive strategies.
10
+ - **Self-Critique**: Actively check for risks, biases, and potential errors in the thinking process.
11
+ - **Branch Merging**: Synthesize and combine insights from multiple divergent reasoning paths.
12
+ - **Hypothesis Testing**: Formulate specific hypotheses and verify them against evidence or logic.
13
+ - **Option Evaluation**: Score and weigh different options (`evaluation`) to make informed decisions.
14
+ - **Self-Reflexion**: Review and correct previous thoughts (`reflexion`) to improve accuracy.
15
+ - **Dynamic Adjustment**: Adjust the total number of thoughts dynamically as understanding deepens.
16
+
17
+ ## Tool
18
+
19
+ ### sequentialthinking
20
+
21
+ Facilitates a detailed, step-by-step thinking process for problem-solving and analysis.
22
+
23
+ **(See inputs above)**
24
+
25
+ ### web_search
26
+
27
+ Search the web using Brave or Exa APIs.
28
+
29
+ **Inputs:**
30
+ - `query` (string): The search query.
31
+ - `provider` (enum, optional): 'brave', 'exa', or 'google'.
32
+
33
+ **Configuration:**
34
+ Requires `BRAVE_API_KEY` or `EXA_API_KEY` environment variables.
35
+
36
+ ### fetch
37
+
38
+ Perform an HTTP request.
39
+
40
+ **Inputs:**
41
+ - `url` (string): URL to fetch.
42
+ - `method` (string, optional): HTTP method (GET, POST, etc.).
43
+ - `headers` (object, optional): Request headers.
44
+ - `body` (string, optional): Request body.
45
+
46
+ ### shell_execute
47
+
48
+ Execute a shell command. **Use with caution.**
49
+
50
+ **Inputs:**
51
+ - `command` (string): The command to run.
52
+
53
+ ### read_file / write_file
54
+
55
+ Manage files on the local system.
56
+
57
+ **Inputs:**
58
+ - `path` (string): File path.
59
+ - `content` (string, for write_file): Content to write.
60
+
61
+ ### build_project_graph
62
+
63
+ Scan directory and build dependency graph.
64
+
65
+ **Inputs:**
66
+ - `path` (string, optional): Root directory to scan.
67
+
68
+ ### get_file_relationships
69
+
70
+ Get imports and references for a specific file.
71
+
72
+ **Inputs:**
73
+ - `filePath` (string): Path to the file.
74
+
75
+ ### get_project_graph_summary
76
+
77
+ Get overview of project structure and most referenced files.
78
+
79
+ ## Usage
80
+
81
+ The Sequential Thinking tool is designed for:
82
+ - Breaking down complex problems into steps
83
+ - Planning and design with room for revision
84
+ - Analysis that might need course correction
85
+ - Problems where the full scope might not be clear initially
86
+ - Tasks that need to maintain context over multiple steps
87
+ - Situations where irrelevant information needs to be filtered out
88
+
89
+ ## Configuration
90
+
91
+ ### Usage with Claude Desktop
92
+
93
+ Add this to your `claude_desktop_config.json`:
94
+
95
+ #### npx
96
+
97
+ ```json
98
+ {
99
+ "mcpServers": {
100
+ "sequential-thinking": {
101
+ "command": "npx",
102
+ "args": [
103
+ "-y",
104
+ "@modelcontextprotocol/server-sequential-thinking"
105
+ ]
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ ### Usage with VS Code
112
+
113
+ For quick installation, click one of the installation buttons below...
114
+
115
+ [![Install with NPX in VS Code](https://img.shields.io/badge/VS_Code-NPM-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=sequentialthinking&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40modelcontextprotocol%2Fserver-sequential-thinking%22%5D%7D)
116
+
117
+ ## Building
118
+
119
+ ```bash
120
+ npm install
121
+ npm run build
122
+ ```
123
+
124
+ ## Testing
125
+
126
+ ```bash
127
+ npm test
128
+ ```
129
+
130
+ ## License
131
+
132
+ This MCP server is licensed under the MIT License.
package/dist/graph.js ADDED
@@ -0,0 +1,120 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+ export class ProjectKnowledgeGraph {
4
+ nodes = new Map();
5
+ rootDir = '';
6
+ constructor() { }
7
+ async build(rootDir) {
8
+ this.rootDir = path.resolve(rootDir);
9
+ this.nodes.clear();
10
+ const files = await this.getAllFiles(this.rootDir);
11
+ // Step 1: Initialize nodes
12
+ for (const file of files) {
13
+ this.nodes.set(file, {
14
+ path: file,
15
+ imports: [],
16
+ importedBy: []
17
+ });
18
+ }
19
+ // Step 2: Parse imports and build edges
20
+ for (const file of files) {
21
+ await this.parseFile(file);
22
+ }
23
+ return {
24
+ nodeCount: this.nodes.size,
25
+ totalFiles: files.length
26
+ };
27
+ }
28
+ async getAllFiles(dir) {
29
+ const entries = await fs.readdir(dir, { withFileTypes: true });
30
+ const files = [];
31
+ for (const entry of entries) {
32
+ const res = path.resolve(dir, entry.name);
33
+ if (entry.isDirectory()) {
34
+ if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'dist')
35
+ continue;
36
+ files.push(...await this.getAllFiles(res));
37
+ }
38
+ else {
39
+ if (/\.(ts|js|tsx|jsx|json)$/.test(entry.name)) {
40
+ files.push(res);
41
+ }
42
+ }
43
+ }
44
+ return files;
45
+ }
46
+ async parseFile(filePath) {
47
+ try {
48
+ const content = await fs.readFile(filePath, 'utf-8');
49
+ const importRegex = /import\s+(?:[\w\s{},*]+from\s+)?['"]([^'"]+)['"]/g;
50
+ const dynamicImportRegex = /import\(['"]([^'"]+)['"]\)/g;
51
+ const requireRegex = /require\(['"]([^'"]+)['"]\)/g;
52
+ const allMatches = [
53
+ ...content.matchAll(importRegex),
54
+ ...content.matchAll(dynamicImportRegex),
55
+ ...content.matchAll(requireRegex)
56
+ ];
57
+ const currentNode = this.nodes.get(filePath);
58
+ if (!currentNode)
59
+ return;
60
+ for (const match of allMatches) {
61
+ const importPath = match[1];
62
+ if (importPath.startsWith('.')) {
63
+ const resolvedPath = await this.resolvePath(path.dirname(filePath), importPath);
64
+ if (resolvedPath && this.nodes.has(resolvedPath)) {
65
+ currentNode.imports.push(resolvedPath);
66
+ this.nodes.get(resolvedPath)?.importedBy.push(filePath);
67
+ }
68
+ }
69
+ }
70
+ }
71
+ catch (error) {
72
+ console.error(`Error parsing file ${filePath}:`, error);
73
+ }
74
+ }
75
+ async resolvePath(dir, relativePath) {
76
+ const extensions = ['', '.ts', '.js', '.tsx', '.jsx', '.json', '/index.ts', '/index.js'];
77
+ const absolutePath = path.resolve(dir, relativePath);
78
+ for (const ext of extensions) {
79
+ const p = absolutePath + ext;
80
+ if (this.nodes.has(p)) {
81
+ return p;
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+ getRelationships(filePath) {
87
+ const absolutePath = path.resolve(this.rootDir, filePath);
88
+ // Try to match exact or with extensions
89
+ let node = this.nodes.get(absolutePath);
90
+ if (!node) {
91
+ // Fallback search
92
+ for (const [key, value] of this.nodes.entries()) {
93
+ if (key.endsWith(filePath)) {
94
+ node = value;
95
+ break;
96
+ }
97
+ }
98
+ }
99
+ if (!node)
100
+ return null;
101
+ return {
102
+ path: node.path,
103
+ imports: node.imports.map(p => path.relative(this.rootDir, p)),
104
+ importedBy: node.importedBy.map(p => path.relative(this.rootDir, p))
105
+ };
106
+ }
107
+ getSummary() {
108
+ return {
109
+ root: this.rootDir,
110
+ fileCount: this.nodes.size,
111
+ mostReferencedFiles: [...this.nodes.values()]
112
+ .sort((a, b) => b.importedBy.length - a.importedBy.length)
113
+ .slice(0, 5)
114
+ .map(n => ({
115
+ file: path.relative(this.rootDir, n.path),
116
+ referencedBy: n.importedBy.length
117
+ }))
118
+ };
119
+ }
120
+ }
package/dist/index.js ADDED
@@ -0,0 +1,301 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import { SequentialThinkingServer } from './lib.js';
6
+ import * as fs from 'fs/promises';
7
+ import { exec } from 'child_process';
8
+ import { promisify } from 'util';
9
+ import { ProjectKnowledgeGraph } from './graph.js';
10
+ const execAsync = promisify(exec);
11
+ const server = new McpServer({
12
+ name: "sequential-thinking-server",
13
+ version: "0.2.0",
14
+ });
15
+ const thinkingServer = new SequentialThinkingServer();
16
+ const knowledgeGraph = new ProjectKnowledgeGraph();
17
+ // --- Sequential Thinking Tool ---
18
+ server.tool("sequentialthinking", `A detailed tool for dynamic and reflective problem-solving through thoughts.
19
+ This tool helps analyze problems through a flexible thinking process that can adapt and evolve.
20
+ Each thought can build on, question, or revise previous insights as understanding deepens.
21
+
22
+ When to use this tool:
23
+ - Breaking down complex problems into steps
24
+ - Planning and design with room for revision
25
+ - Analysis that might need course correction
26
+ - Problems where the full scope might not be clear initially
27
+ - Problems that require a multi-step solution
28
+ - Tasks that need to maintain context over multiple steps
29
+ - Situations where irrelevant information needs to be filtered out
30
+
31
+ Key features:
32
+ - You can adjust total_thoughts up or down as you progress
33
+ - You can question or revise previous thoughts
34
+ - You can add more thoughts even after reaching what seemed like the end
35
+ - You can express uncertainty and explore alternative approaches
36
+ - Not every thought needs to build linearly - you can branch or backtrack
37
+ - Iterative Reasoning: Think step-by-step in a structured manner
38
+ - Tree of Thoughts: Generate and evaluate multiple options (Conservative/Balanced/Aggressive)
39
+ - Self-Critique: Check for risks, biases, and errors in thinking
40
+ - Branch Merging: Combine insights from multiple divergent paths
41
+ - Hypothesis Testing: Formulate and verify hypotheses
42
+ - Generates a solution hypothesis
43
+ - Verifies the hypothesis based on the Chain of Thought steps
44
+ - Repeats the process until satisfied
45
+ - Provides a correct answer
46
+
47
+ Parameters explained:
48
+ - thought: Your current thinking step, which can include:
49
+ * Regular analytical steps
50
+ * Revisions of previous thoughts
51
+ * Questions about previous decisions
52
+ * Realizations about needing more analysis
53
+ * Changes in approach
54
+ * Hypothesis generation
55
+ * Hypothesis verification
56
+ - nextThoughtNeeded: True if you need more thinking, even if at what seemed like the end
57
+ - thoughtNumber: Current number in sequence (can go beyond initial total if needed)
58
+ - totalThoughts: Current estimate of thoughts needed (can be adjusted up/down)
59
+ - isRevision: A boolean indicating if this thought revises previous thinking
60
+ - revisesThought: If is_revision is true, which thought number is being reconsidered
61
+ - branchFromThought: If branching, which thought number is the branching point
62
+ - branchId: Identifier for the current branch (if any)
63
+ - needsMoreThoughts: If reaching end but realizing more thoughts needed
64
+ - thoughtType: The type of thought (analysis, generation, evaluation, reflexion, selection)
65
+ - score: Score for evaluation (1-10)
66
+ - options: List of options generated
67
+ - selectedOption: The option selected
68
+
69
+ You should:
70
+ 1. Start with an initial estimate of needed thoughts, but be ready to adjust
71
+ 2. Feel free to question or revise previous thoughts
72
+ 3. Don't hesitate to add more thoughts if needed, even at the "end"
73
+ 4. Express uncertainty when present
74
+ 5. Mark thoughts that revise previous thinking or branch into new paths
75
+ 6. Ignore information that is irrelevant to the current step
76
+ 7. Generate a solution hypothesis when appropriate
77
+ 8. Verify the hypothesis based on the Chain of Thought steps
78
+ 9. Repeat the process until satisfied with the solution
79
+ 10. Provide a single, ideally correct answer as the final output
80
+ 11. Only set nextThoughtNeeded to false when truly done and a satisfactory answer is reached`, {
81
+ thought: z.string().describe("Your current thinking step"),
82
+ nextThoughtNeeded: z.boolean().describe("Whether another thought step is needed"),
83
+ thoughtNumber: z.number().int().min(1).describe("Current thought number (numeric value, e.g., 1, 2, 3)"),
84
+ totalThoughts: z.number().int().min(1).describe("Estimated total thoughts needed (numeric value, e.g., 5, 10)"),
85
+ isRevision: z.boolean().optional().describe("Whether this revises previous thinking"),
86
+ revisesThought: z.number().int().min(1).optional().describe("Which thought is being reconsidered"),
87
+ branchFromThought: z.number().int().min(1).optional().describe("Branching point thought number"),
88
+ branchId: z.string().optional().describe("Branch identifier"),
89
+ needsMoreThoughts: z.boolean().optional().describe("If more thoughts are needed"),
90
+ thoughtType: z.enum(['analysis', 'generation', 'evaluation', 'reflexion', 'selection']).optional().describe("The type of thought"),
91
+ score: z.number().min(1).max(10).optional().describe("Score for evaluation (1-10)"),
92
+ options: z.array(z.string()).optional().describe("List of options generated"),
93
+ selectedOption: z.string().optional().describe("The option selected")
94
+ }, async (args) => {
95
+ const result = thinkingServer.processThought(args);
96
+ return {
97
+ content: result.content,
98
+ isError: result.isError
99
+ };
100
+ });
101
+ // --- New Tools ---
102
+ // 1. web_search
103
+ server.tool("web_search", "Search the web using Brave or Exa APIs (requires API keys in environment variables: BRAVE_API_KEY or EXA_API_KEY).", {
104
+ query: z.string().describe("The search query"),
105
+ provider: z.enum(['brave', 'exa', 'google']).optional().describe("Preferred search provider")
106
+ }, async ({ query, provider }) => {
107
+ try {
108
+ // Priority: User Preference > Brave > Exa
109
+ let selectedProvider = provider;
110
+ if (!selectedProvider) {
111
+ if (process.env.BRAVE_API_KEY)
112
+ selectedProvider = 'brave';
113
+ else if (process.env.EXA_API_KEY)
114
+ selectedProvider = 'exa';
115
+ else
116
+ return { content: [{ type: "text", text: "Error: No search provider configured. Please set BRAVE_API_KEY or EXA_API_KEY." }], isError: true };
117
+ }
118
+ if (selectedProvider === 'brave') {
119
+ if (!process.env.BRAVE_API_KEY)
120
+ throw new Error("BRAVE_API_KEY not found");
121
+ const response = await fetch(`https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(query)}&count=5`, {
122
+ headers: { 'X-Subscription-Token': process.env.BRAVE_API_KEY }
123
+ });
124
+ if (!response.ok)
125
+ throw new Error(`Brave API error: ${response.statusText}`);
126
+ const data = await response.json();
127
+ return { content: [{ type: "text", text: JSON.stringify(data.web?.results || data, null, 2) }] };
128
+ }
129
+ if (selectedProvider === 'exa') {
130
+ if (!process.env.EXA_API_KEY)
131
+ throw new Error("EXA_API_KEY not found");
132
+ const response = await fetch('https://api.exa.ai/search', {
133
+ method: 'POST',
134
+ headers: {
135
+ 'x-api-key': process.env.EXA_API_KEY,
136
+ 'Content-Type': 'application/json'
137
+ },
138
+ body: JSON.stringify({ query, numResults: 5 })
139
+ });
140
+ if (!response.ok)
141
+ throw new Error(`Exa API error: ${response.statusText}`);
142
+ const data = await response.json();
143
+ return { content: [{ type: "text", text: JSON.stringify(data.results || data, null, 2) }] };
144
+ }
145
+ return { content: [{ type: "text", text: "Error: Unsupported or unconfigured provider." }], isError: true };
146
+ }
147
+ catch (error) {
148
+ return {
149
+ content: [{ type: "text", text: `Search Error: ${error instanceof Error ? error.message : String(error)}` }],
150
+ isError: true
151
+ };
152
+ }
153
+ });
154
+ // 2. fetch
155
+ server.tool("fetch", "Perform an HTTP request to a specific URL.", {
156
+ url: z.string().url().describe("The URL to fetch"),
157
+ method: z.enum(['GET', 'POST', 'PUT', 'DELETE']).optional().default('GET').describe("HTTP Method"),
158
+ headers: z.record(z.string(), z.string()).optional().describe("HTTP Headers"),
159
+ body: z.string().optional().describe("Request body (for POST/PUT)")
160
+ }, async ({ url, method, headers, body }) => {
161
+ try {
162
+ const response = await fetch(url, {
163
+ method,
164
+ headers: headers || {},
165
+ body: body
166
+ });
167
+ const text = await response.text();
168
+ return {
169
+ content: [{
170
+ type: "text",
171
+ text: `Status: ${response.status}\n\n${text.substring(0, 10000)}${text.length > 10000 ? '\n...(truncated)' : ''}`
172
+ }]
173
+ };
174
+ }
175
+ catch (error) {
176
+ return {
177
+ content: [{ type: "text", text: `Fetch Error: ${error instanceof Error ? error.message : String(error)}` }],
178
+ isError: true
179
+ };
180
+ }
181
+ });
182
+ // 3. shell_execute
183
+ server.tool("shell_execute", "Execute a shell command. Use with caution.", {
184
+ command: z.string().describe("The bash command to execute")
185
+ }, async ({ command }) => {
186
+ try {
187
+ const { stdout, stderr } = await execAsync(command);
188
+ return {
189
+ content: [{
190
+ type: "text",
191
+ text: `STDOUT:\n${stdout}\n\nSTDERR:\n${stderr}`
192
+ }]
193
+ };
194
+ }
195
+ catch (error) {
196
+ return {
197
+ content: [{ type: "text", text: `Shell Error: ${error instanceof Error ? error.message : String(error)}` }],
198
+ isError: true
199
+ };
200
+ }
201
+ });
202
+ // 4. read_file
203
+ server.tool("read_file", "Read the contents of a file.", {
204
+ path: z.string().describe("Path to the file")
205
+ }, async ({ path }) => {
206
+ try {
207
+ const content = await fs.readFile(path, 'utf-8');
208
+ return {
209
+ content: [{ type: "text", text: content }]
210
+ };
211
+ }
212
+ catch (error) {
213
+ return {
214
+ content: [{ type: "text", text: `Read Error: ${error instanceof Error ? error.message : String(error)}` }],
215
+ isError: true
216
+ };
217
+ }
218
+ });
219
+ // 5. write_file
220
+ server.tool("write_file", "Write content to a file (overwrites existing).", {
221
+ path: z.string().describe("Path to the file"),
222
+ content: z.string().describe("Content to write")
223
+ }, async ({ path, content }) => {
224
+ try {
225
+ await fs.writeFile(path, content, 'utf-8');
226
+ return {
227
+ content: [{ type: "text", text: `Successfully wrote to ${path}` }]
228
+ };
229
+ }
230
+ catch (error) {
231
+ return {
232
+ content: [{ type: "text", text: `Write Error: ${error instanceof Error ? error.message : String(error)}` }],
233
+ isError: true
234
+ };
235
+ }
236
+ });
237
+ // --- Project Knowledge Graph Tools ---
238
+ // 6. build_project_graph
239
+ server.tool("build_project_graph", "Scan the directory and build a dependency graph of the project (Analyzing imports/exports).", {
240
+ path: z.string().optional().default('.').describe("Root directory path to scan (default: current dir)")
241
+ }, async ({ path }) => {
242
+ try {
243
+ const result = await knowledgeGraph.build(path || '.');
244
+ return {
245
+ content: [{ type: "text", text: `Graph built successfully.\nNodes: ${result.nodeCount}\nTotal Scanned Files: ${result.totalFiles}` }]
246
+ };
247
+ }
248
+ catch (error) {
249
+ return {
250
+ content: [{ type: "text", text: `Graph Build Error: ${error instanceof Error ? error.message : String(error)}` }],
251
+ isError: true
252
+ };
253
+ }
254
+ });
255
+ // 7. get_file_relationships
256
+ server.tool("get_file_relationships", "Get dependencies and references for a specific file from the built graph.", {
257
+ filePath: z.string().describe("Path to the file (e.g., 'src/index.ts')")
258
+ }, async ({ filePath }) => {
259
+ try {
260
+ const rel = knowledgeGraph.getRelationships(filePath);
261
+ if (!rel) {
262
+ return {
263
+ content: [{ type: "text", text: `File not found in graph: ${filePath}. (Did you run 'build_project_graph'?)` }],
264
+ isError: true
265
+ };
266
+ }
267
+ return {
268
+ content: [{ type: "text", text: JSON.stringify(rel, null, 2) }]
269
+ };
270
+ }
271
+ catch (error) {
272
+ return {
273
+ content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }],
274
+ isError: true
275
+ };
276
+ }
277
+ });
278
+ // 8. get_project_graph_summary
279
+ server.tool("get_project_graph_summary", "Get a summary of the project structure (most referenced files, total count).", {}, async () => {
280
+ try {
281
+ const summary = knowledgeGraph.getSummary();
282
+ return {
283
+ content: [{ type: "text", text: JSON.stringify(summary, null, 2) }]
284
+ };
285
+ }
286
+ catch (error) {
287
+ return {
288
+ content: [{ type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` }],
289
+ isError: true
290
+ };
291
+ }
292
+ });
293
+ async function runServer() {
294
+ const transport = new StdioServerTransport();
295
+ await server.connect(transport);
296
+ console.error("Sequential Thinking MCP Server (Extended) running on stdio");
297
+ }
298
+ runServer().catch((error) => {
299
+ console.error("Fatal error running server:", error);
300
+ process.exit(1);
301
+ });
package/dist/lib.js ADDED
@@ -0,0 +1,97 @@
1
+ import chalk from 'chalk';
2
+ export class SequentialThinkingServer {
3
+ thoughtHistory = [];
4
+ branches = {};
5
+ disableThoughtLogging;
6
+ constructor() {
7
+ this.disableThoughtLogging = (process.env.DISABLE_THOUGHT_LOGGING || "").toLowerCase() === "true";
8
+ }
9
+ formatThought(thoughtData) {
10
+ const { thoughtNumber, totalThoughts, thought, isRevision, revisesThought, branchFromThought, branchId, thoughtType, score, options, selectedOption } = thoughtData;
11
+ let prefix = '';
12
+ let context = '';
13
+ if (thoughtType === 'reflexion' || isRevision) {
14
+ prefix = chalk.yellow('🔄 Reflexion');
15
+ if (revisesThought)
16
+ context += ` (revising thought ${revisesThought})`;
17
+ }
18
+ else if (thoughtType === 'generation') {
19
+ prefix = chalk.magenta('💡 Generation');
20
+ }
21
+ else if (thoughtType === 'evaluation') {
22
+ prefix = chalk.cyan('⚖️ Evaluation');
23
+ if (score)
24
+ context += ` (Score: ${score})`;
25
+ }
26
+ else if (thoughtType === 'selection') {
27
+ prefix = chalk.green('✅ Selection');
28
+ if (selectedOption)
29
+ context += ` (Selected: ${selectedOption})`;
30
+ }
31
+ else if (branchFromThought) {
32
+ prefix = chalk.green('🌿 Branch');
33
+ context = ` (from thought ${branchFromThought}, ID: ${branchId})`;
34
+ }
35
+ else {
36
+ prefix = chalk.blue('💭 Thought');
37
+ context = '';
38
+ }
39
+ const header = `${prefix} ${thoughtNumber}/${totalThoughts}${context}`;
40
+ const borderLength = Math.max(header.length, thought.length) + 4;
41
+ const border = '─'.repeat(borderLength);
42
+ let extraContent = '';
43
+ if (options && options.length > 0) {
44
+ extraContent += `
45
+ │ Options:
46
+ ` + options.map(o => `│ - ${o}`).join('\n');
47
+ }
48
+ return `
49
+ ┌${border}┐
50
+ │ ${header} │
51
+ ├${border}┤
52
+ │ ${thought.padEnd(borderLength - 2)} │${extraContent}
53
+ └${border}┘`;
54
+ }
55
+ processThought(input) {
56
+ try {
57
+ if (input.thoughtNumber > input.totalThoughts) {
58
+ input.totalThoughts = input.thoughtNumber;
59
+ }
60
+ this.thoughtHistory.push(input);
61
+ if (input.branchFromThought && input.branchId) {
62
+ if (!this.branches[input.branchId]) {
63
+ this.branches[input.branchId] = [];
64
+ }
65
+ this.branches[input.branchId].push(input);
66
+ }
67
+ if (!this.disableThoughtLogging) {
68
+ const formattedThought = this.formatThought(input);
69
+ console.error(formattedThought);
70
+ }
71
+ return {
72
+ content: [{
73
+ type: "text",
74
+ text: JSON.stringify({
75
+ thoughtNumber: input.thoughtNumber,
76
+ totalThoughts: input.totalThoughts,
77
+ nextThoughtNeeded: input.nextThoughtNeeded,
78
+ branches: Object.keys(this.branches),
79
+ thoughtHistoryLength: this.thoughtHistory.length
80
+ }, null, 2)
81
+ }]
82
+ };
83
+ }
84
+ catch (error) {
85
+ return {
86
+ content: [{
87
+ type: "text",
88
+ text: JSON.stringify({
89
+ error: error instanceof Error ? error.message : String(error),
90
+ status: 'failed'
91
+ }, null, 2)
92
+ }],
93
+ isError: true
94
+ };
95
+ }
96
+ }
97
+ }
@@ -0,0 +1,85 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { SequentialThinkingServer } from './lib.js';
3
+ describe('SequentialThinkingServer', () => {
4
+ let server;
5
+ beforeEach(() => {
6
+ server = new SequentialThinkingServer();
7
+ });
8
+ it('should process a basic linear thought', () => {
9
+ const input = {
10
+ thought: "First step",
11
+ thoughtNumber: 1,
12
+ totalThoughts: 3,
13
+ nextThoughtNeeded: true,
14
+ thoughtType: 'analysis'
15
+ };
16
+ const result = server.processThought(input);
17
+ expect(result.isError).toBeUndefined();
18
+ const content = JSON.parse(result.content[0].text);
19
+ expect(content.thoughtNumber).toBe(1);
20
+ expect(content.thoughtHistoryLength).toBe(1);
21
+ });
22
+ it('should handle branching correctly', () => {
23
+ // Initial thought
24
+ server.processThought({
25
+ thought: "Root thought",
26
+ thoughtNumber: 1,
27
+ totalThoughts: 3,
28
+ nextThoughtNeeded: true
29
+ });
30
+ // Branch 1
31
+ const branch1Input = {
32
+ thought: "Alternative A",
33
+ thoughtNumber: 2,
34
+ totalThoughts: 3,
35
+ nextThoughtNeeded: true,
36
+ branchFromThought: 1,
37
+ branchId: "branch-A",
38
+ thoughtType: 'generation'
39
+ };
40
+ const result1 = server.processThought(branch1Input);
41
+ const content1 = JSON.parse(result1.content[0].text);
42
+ expect(content1.branches).toContain("branch-A");
43
+ // Branch 2
44
+ const branch2Input = {
45
+ thought: "Alternative B",
46
+ thoughtNumber: 2,
47
+ totalThoughts: 3,
48
+ nextThoughtNeeded: true,
49
+ branchFromThought: 1,
50
+ branchId: "branch-B",
51
+ thoughtType: 'generation'
52
+ };
53
+ const result2 = server.processThought(branch2Input);
54
+ const content2 = JSON.parse(result2.content[0].text);
55
+ expect(content2.branches).toContain("branch-B");
56
+ expect(content2.branches.length).toBe(2);
57
+ });
58
+ it('should handle evaluation with scores', () => {
59
+ const input = {
60
+ thought: "Evaluating option X",
61
+ thoughtNumber: 3,
62
+ totalThoughts: 5,
63
+ nextThoughtNeeded: true,
64
+ thoughtType: 'evaluation',
65
+ score: 8,
66
+ options: ['Option X', 'Option Y']
67
+ };
68
+ const result = server.processThought(input);
69
+ expect(result.isError).toBeUndefined();
70
+ // Since we don't return the score in the simple JSON response (only in logs or history),
71
+ // we mainly check that it doesn't crash and processes correctly.
72
+ // If we exposed history in the response, we could check that too.
73
+ });
74
+ it('should adjust totalThoughts if thoughtNumber exceeds it', () => {
75
+ const input = {
76
+ thought: "Unexpected long process",
77
+ thoughtNumber: 6,
78
+ totalThoughts: 5,
79
+ nextThoughtNeeded: true
80
+ };
81
+ const result = server.processThought(input);
82
+ const content = JSON.parse(result.content[0].text);
83
+ expect(content.totalThoughts).toBe(6);
84
+ });
85
+ });
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@gotza02/sequential-thinking",
3
+ "version": "2026.01.16",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "MCP server for sequential thinking and problem solving (Extended with Web Search & Graph)",
8
+ "license": "MIT",
9
+ "mcpName": "sequential-thinking-extended",
10
+ "author": "Anthropic, PBC (https://anthropic.com)",
11
+ "homepage": "https://modelcontextprotocol.io",
12
+ "bugs": "https://github.com/modelcontextprotocol/servers/issues",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/modelcontextprotocol/servers.git"
16
+ },
17
+ "type": "module",
18
+ "bin": {
19
+ "mcp-server-sequential-thinking": "dist/index.js"
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsc && shx chmod +x dist/*.js",
26
+ "prepare": "npm run build",
27
+ "watch": "tsc --watch",
28
+ "test": "vitest run --coverage"
29
+ },
30
+ "dependencies": {
31
+ "@modelcontextprotocol/sdk": "^1.24.0",
32
+ "chalk": "^5.3.0",
33
+ "yargs": "^17.7.2"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^22",
37
+ "@types/yargs": "^17.0.32",
38
+ "@vitest/coverage-v8": "^2.1.8",
39
+ "shx": "^0.3.4",
40
+ "typescript": "^5.3.3",
41
+ "vitest": "^2.1.8"
42
+ }
43
+ }