@gotza02/sequential-thinking 2026.1.20 → 2026.1.21
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 +22 -0
- package/dist/graph.js +37 -18
- package/dist/index.js +2 -2
- package/dist/lib.js +47 -13
- package/dist/server.test.js +22 -12
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,6 +14,16 @@ An MCP server implementation that provides a tool for dynamic and reflective pro
|
|
|
14
14
|
- **Self-Reflexion**: Review and correct previous thoughts (`reflexion`) to improve accuracy.
|
|
15
15
|
- **Dynamic Adjustment**: Adjust the total number of thoughts dynamically as understanding deepens.
|
|
16
16
|
|
|
17
|
+
## System Instruction for AI
|
|
18
|
+
|
|
19
|
+
When connected to this server, you should follow these guidelines to maximize problem-solving effectiveness:
|
|
20
|
+
|
|
21
|
+
1. **Iterative Thinking**: Use the `sequentialthinking` tool for every complex task. Do not try to provide a final answer immediately. Instead, break the problem into steps, generate hypotheses, and evaluate them.
|
|
22
|
+
2. **Context Discovery**: Before modifying code, always use `build_project_graph` followed by `get_project_graph_summary` to understand the project architecture. Use `get_file_relationships` to see how specific files are connected.
|
|
23
|
+
3. **Fact Verification**: Use `web_search` to verify current facts, library versions, or documentation. Use `fetch` to retrieve raw data from external sources when needed.
|
|
24
|
+
4. **Local Execution**: Use `shell_execute` to run tests, linters, or build commands to verify your changes. Always read files using `read_file` before attempting to write or modify them.
|
|
25
|
+
5. **Persistence**: The thinking process is saved automatically. You can resume previous sessions by reviewing the `thoughts_history.json` file if needed.
|
|
26
|
+
|
|
17
27
|
## Tool
|
|
18
28
|
|
|
19
29
|
### sequentialthinking
|
|
@@ -130,6 +140,18 @@ npm run build
|
|
|
130
140
|
npm test
|
|
131
141
|
```
|
|
132
142
|
|
|
143
|
+
## Recent Updates (v2026.1.21)
|
|
144
|
+
|
|
145
|
+
- **Performance & Accuracy**:
|
|
146
|
+
- Replaced Regex-based code analysis with **TypeScript Compiler API (AST)** for 100% accurate import/export detection.
|
|
147
|
+
- Improved `web_search` robustness and error handling.
|
|
148
|
+
- **Persistence**:
|
|
149
|
+
- Implemented **File-based Persistence** for the thinking process. Your thoughts are now saved to `thoughts_history.json` automatically.
|
|
150
|
+
- Switched to **Asynchronous File I/O** to prevent server blocking.
|
|
151
|
+
- **Bug Fixes**:
|
|
152
|
+
- Fixed duplicate import entries in the project graph.
|
|
153
|
+
- Resolved memory growth issues in long-running sessions.
|
|
154
|
+
|
|
133
155
|
## Recent Updates (v2026.1.20)
|
|
134
156
|
|
|
135
157
|
- **New Features**:
|
package/dist/graph.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from 'fs/promises';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
+
import ts from 'typescript';
|
|
3
4
|
export class ProjectKnowledgeGraph {
|
|
4
5
|
nodes = new Map();
|
|
5
6
|
rootDir = '';
|
|
@@ -45,30 +46,48 @@ export class ProjectKnowledgeGraph {
|
|
|
45
46
|
}
|
|
46
47
|
async parseFile(filePath) {
|
|
47
48
|
try {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
50
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
51
|
+
const imports = [];
|
|
52
|
+
const visit = (node) => {
|
|
53
|
+
// 1. Static imports: import ... from '...'
|
|
54
|
+
if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) {
|
|
55
|
+
if (node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
56
|
+
imports.push(node.moduleSpecifier.text);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// 2. Dynamic imports: import('...')
|
|
60
|
+
else if (ts.isCallExpression(node)) {
|
|
61
|
+
if (node.expression.kind === ts.SyntaxKind.ImportKeyword && node.arguments.length > 0) {
|
|
62
|
+
const arg = node.arguments[0];
|
|
63
|
+
if (ts.isStringLiteral(arg)) {
|
|
64
|
+
imports.push(arg.text);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// 3. CommonJS: require('...')
|
|
68
|
+
else if (ts.isIdentifier(node.expression) && node.expression.text === 'require' && node.arguments.length > 0) {
|
|
69
|
+
const arg = node.arguments[0];
|
|
70
|
+
if (ts.isStringLiteral(arg)) {
|
|
71
|
+
imports.push(arg.text);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
ts.forEachChild(node, visit);
|
|
76
|
+
};
|
|
77
|
+
visit(sourceFile);
|
|
62
78
|
const currentNode = this.nodes.get(filePath);
|
|
63
79
|
if (!currentNode)
|
|
64
80
|
return;
|
|
65
|
-
for (const
|
|
66
|
-
const importPath = match[1];
|
|
81
|
+
for (const importPath of imports) {
|
|
67
82
|
if (importPath.startsWith('.')) {
|
|
68
83
|
const resolvedPath = await this.resolvePath(path.dirname(filePath), importPath);
|
|
69
84
|
if (resolvedPath && this.nodes.has(resolvedPath)) {
|
|
70
|
-
currentNode.imports.
|
|
71
|
-
|
|
85
|
+
if (!currentNode.imports.includes(resolvedPath)) {
|
|
86
|
+
currentNode.imports.push(resolvedPath);
|
|
87
|
+
}
|
|
88
|
+
if (!this.nodes.get(resolvedPath)?.importedBy.includes(filePath)) {
|
|
89
|
+
this.nodes.get(resolvedPath)?.importedBy.push(filePath);
|
|
90
|
+
}
|
|
72
91
|
}
|
|
73
92
|
}
|
|
74
93
|
}
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ const server = new McpServer({
|
|
|
12
12
|
name: "sequential-thinking-server",
|
|
13
13
|
version: "2026.1.18",
|
|
14
14
|
});
|
|
15
|
-
const thinkingServer = new SequentialThinkingServer();
|
|
15
|
+
const thinkingServer = new SequentialThinkingServer(process.env.THOUGHTS_STORAGE_PATH || 'thoughts_history.json');
|
|
16
16
|
const knowledgeGraph = new ProjectKnowledgeGraph();
|
|
17
17
|
// --- Sequential Thinking Tool ---
|
|
18
18
|
server.tool("sequentialthinking", `A detailed tool for dynamic and reflective problem-solving through thoughts.
|
|
@@ -92,7 +92,7 @@ You should:
|
|
|
92
92
|
options: z.array(z.string()).optional().describe("List of options generated"),
|
|
93
93
|
selectedOption: z.string().optional().describe("The option selected")
|
|
94
94
|
}, async (args) => {
|
|
95
|
-
const result = thinkingServer.processThought(args);
|
|
95
|
+
const result = await thinkingServer.processThought(args);
|
|
96
96
|
return {
|
|
97
97
|
content: result.content,
|
|
98
98
|
isError: result.isError
|
package/dist/lib.js
CHANGED
|
@@ -1,10 +1,53 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
import * as fs from 'fs/promises';
|
|
3
|
+
import { existsSync, readFileSync } from 'fs';
|
|
4
|
+
import * as path from 'path';
|
|
2
5
|
export class SequentialThinkingServer {
|
|
3
6
|
thoughtHistory = [];
|
|
4
7
|
branches = {};
|
|
5
8
|
disableThoughtLogging;
|
|
6
|
-
|
|
9
|
+
storagePath;
|
|
10
|
+
constructor(storagePath = 'thoughts_history.json') {
|
|
7
11
|
this.disableThoughtLogging = (process.env.DISABLE_THOUGHT_LOGGING || "").toLowerCase() === "true";
|
|
12
|
+
this.storagePath = path.resolve(storagePath);
|
|
13
|
+
this.loadHistory();
|
|
14
|
+
}
|
|
15
|
+
loadHistory() {
|
|
16
|
+
try {
|
|
17
|
+
if (existsSync(this.storagePath)) {
|
|
18
|
+
const data = readFileSync(this.storagePath, 'utf-8');
|
|
19
|
+
const history = JSON.parse(data);
|
|
20
|
+
if (Array.isArray(history)) {
|
|
21
|
+
this.thoughtHistory = []; // Reset to avoid duplicates
|
|
22
|
+
this.branches = {};
|
|
23
|
+
history.forEach(thought => this.addToMemory(thought));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.error(`Error loading history from ${this.storagePath}:`, error);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async saveHistory() {
|
|
32
|
+
try {
|
|
33
|
+
await fs.writeFile(this.storagePath, JSON.stringify(this.thoughtHistory, null, 2), 'utf-8');
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error(`Error saving history to ${this.storagePath}:`, error);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
addToMemory(input) {
|
|
40
|
+
if (input.thoughtNumber > input.totalThoughts) {
|
|
41
|
+
input.totalThoughts = input.thoughtNumber;
|
|
42
|
+
}
|
|
43
|
+
this.thoughtHistory.push(input);
|
|
44
|
+
if (input.branchFromThought && input.branchId) {
|
|
45
|
+
const branchKey = `${input.branchFromThought}-${input.branchId}`;
|
|
46
|
+
if (!this.branches[branchKey]) {
|
|
47
|
+
this.branches[branchKey] = [];
|
|
48
|
+
}
|
|
49
|
+
this.branches[branchKey].push(input);
|
|
50
|
+
}
|
|
8
51
|
}
|
|
9
52
|
formatThought(thoughtData) {
|
|
10
53
|
const { thoughtNumber, totalThoughts, thought, isRevision, revisesThought, branchFromThought, branchId, thoughtType, score, options, selectedOption } = thoughtData;
|
|
@@ -52,19 +95,10 @@ export class SequentialThinkingServer {
|
|
|
52
95
|
│ ${thought.padEnd(borderLength - 2)} │${extraContent}
|
|
53
96
|
└${border}┘`;
|
|
54
97
|
}
|
|
55
|
-
processThought(input) {
|
|
98
|
+
async processThought(input) {
|
|
56
99
|
try {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
this.thoughtHistory.push(input);
|
|
61
|
-
if (input.branchFromThought && input.branchId) {
|
|
62
|
-
const branchKey = `${input.branchFromThought}-${input.branchId}`;
|
|
63
|
-
if (!this.branches[branchKey]) {
|
|
64
|
-
this.branches[branchKey] = [];
|
|
65
|
-
}
|
|
66
|
-
this.branches[branchKey].push(input);
|
|
67
|
-
}
|
|
100
|
+
this.addToMemory(input);
|
|
101
|
+
await this.saveHistory();
|
|
68
102
|
if (!this.disableThoughtLogging) {
|
|
69
103
|
const formattedThought = this.formatThought(input);
|
|
70
104
|
console.error(formattedThought);
|
package/dist/server.test.js
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
1
|
+
import { describe, it, expect, beforeEach, afterAll } from 'vitest';
|
|
2
2
|
import { SequentialThinkingServer } from './lib.js';
|
|
3
|
+
import * as fs from 'fs';
|
|
3
4
|
describe('SequentialThinkingServer', () => {
|
|
4
5
|
let server;
|
|
6
|
+
const testStoragePath = 'test_thoughts.json';
|
|
5
7
|
beforeEach(() => {
|
|
6
|
-
|
|
8
|
+
if (fs.existsSync(testStoragePath)) {
|
|
9
|
+
fs.unlinkSync(testStoragePath);
|
|
10
|
+
}
|
|
11
|
+
server = new SequentialThinkingServer(testStoragePath);
|
|
7
12
|
});
|
|
8
|
-
|
|
13
|
+
afterAll(() => {
|
|
14
|
+
if (fs.existsSync(testStoragePath)) {
|
|
15
|
+
fs.unlinkSync(testStoragePath);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
it('should process a basic linear thought', async () => {
|
|
9
19
|
const input = {
|
|
10
20
|
thought: "First step",
|
|
11
21
|
thoughtNumber: 1,
|
|
@@ -13,15 +23,15 @@ describe('SequentialThinkingServer', () => {
|
|
|
13
23
|
nextThoughtNeeded: true,
|
|
14
24
|
thoughtType: 'analysis'
|
|
15
25
|
};
|
|
16
|
-
const result = server.processThought(input);
|
|
26
|
+
const result = await server.processThought(input);
|
|
17
27
|
expect(result.isError).toBeUndefined();
|
|
18
28
|
const content = JSON.parse(result.content[0].text);
|
|
19
29
|
expect(content.thoughtNumber).toBe(1);
|
|
20
30
|
expect(content.thoughtHistoryLength).toBe(1);
|
|
21
31
|
});
|
|
22
|
-
it('should handle branching correctly', () => {
|
|
32
|
+
it('should handle branching correctly', async () => {
|
|
23
33
|
// Initial thought
|
|
24
|
-
server.processThought({
|
|
34
|
+
await server.processThought({
|
|
25
35
|
thought: "Root thought",
|
|
26
36
|
thoughtNumber: 1,
|
|
27
37
|
totalThoughts: 3,
|
|
@@ -37,7 +47,7 @@ describe('SequentialThinkingServer', () => {
|
|
|
37
47
|
branchId: "branch-A",
|
|
38
48
|
thoughtType: 'generation'
|
|
39
49
|
};
|
|
40
|
-
const result1 = server.processThought(branch1Input);
|
|
50
|
+
const result1 = await server.processThought(branch1Input);
|
|
41
51
|
const content1 = JSON.parse(result1.content[0].text);
|
|
42
52
|
expect(content1.branches).toContain("1-branch-A");
|
|
43
53
|
// Branch 2
|
|
@@ -50,12 +60,12 @@ describe('SequentialThinkingServer', () => {
|
|
|
50
60
|
branchId: "branch-B",
|
|
51
61
|
thoughtType: 'generation'
|
|
52
62
|
};
|
|
53
|
-
const result2 = server.processThought(branch2Input);
|
|
63
|
+
const result2 = await server.processThought(branch2Input);
|
|
54
64
|
const content2 = JSON.parse(result2.content[0].text);
|
|
55
65
|
expect(content2.branches).toContain("1-branch-B");
|
|
56
66
|
expect(content2.branches.length).toBe(2);
|
|
57
67
|
});
|
|
58
|
-
it('should handle evaluation with scores', () => {
|
|
68
|
+
it('should handle evaluation with scores', async () => {
|
|
59
69
|
const input = {
|
|
60
70
|
thought: "Evaluating option X",
|
|
61
71
|
thoughtNumber: 3,
|
|
@@ -65,20 +75,20 @@ describe('SequentialThinkingServer', () => {
|
|
|
65
75
|
score: 8,
|
|
66
76
|
options: ['Option X', 'Option Y']
|
|
67
77
|
};
|
|
68
|
-
const result = server.processThought(input);
|
|
78
|
+
const result = await server.processThought(input);
|
|
69
79
|
expect(result.isError).toBeUndefined();
|
|
70
80
|
// Since we don't return the score in the simple JSON response (only in logs or history),
|
|
71
81
|
// we mainly check that it doesn't crash and processes correctly.
|
|
72
82
|
// If we exposed history in the response, we could check that too.
|
|
73
83
|
});
|
|
74
|
-
it('should adjust totalThoughts if thoughtNumber exceeds it', () => {
|
|
84
|
+
it('should adjust totalThoughts if thoughtNumber exceeds it', async () => {
|
|
75
85
|
const input = {
|
|
76
86
|
thought: "Unexpected long process",
|
|
77
87
|
thoughtNumber: 6,
|
|
78
88
|
totalThoughts: 5,
|
|
79
89
|
nextThoughtNeeded: true
|
|
80
90
|
};
|
|
81
|
-
const result = server.processThought(input);
|
|
91
|
+
const result = await server.processThought(input);
|
|
82
92
|
const content = JSON.parse(result.content[0].text);
|
|
83
93
|
expect(content.totalThoughts).toBe(6);
|
|
84
94
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gotza02/sequential-thinking",
|
|
3
|
-
"version": "2026.1.
|
|
3
|
+
"version": "2026.1.21",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@modelcontextprotocol/sdk": "^1.24.0",
|
|
32
32
|
"chalk": "^5.3.0",
|
|
33
|
+
"typescript": "^5.3.3",
|
|
33
34
|
"yargs": "^17.7.2"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
@@ -37,7 +38,6 @@
|
|
|
37
38
|
"@types/yargs": "^17.0.32",
|
|
38
39
|
"@vitest/coverage-v8": "^2.1.8",
|
|
39
40
|
"shx": "^0.3.4",
|
|
40
|
-
"typescript": "^5.3.3",
|
|
41
41
|
"vitest": "^2.1.8"
|
|
42
42
|
}
|
|
43
43
|
}
|