@gotza02/sequential-thinking 2026.2.0 → 2026.2.2
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 +189 -177
- package/dist/tools/web.js +79 -60
- package/dist/web_fallback.test.js +103 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,225 +1,237 @@
|
|
|
1
|
-
# Sequential Thinking MCP Server
|
|
1
|
+
# Sequential Thinking MCP Server (Extended Edition) 🧠✨
|
|
2
2
|
|
|
3
|
-
MCP Server
|
|
3
|
+
**MCP Server ที่ช่วยให้ AI "คิดก่อนทำ" อย่างเป็นระบบ พร้อมความสามารถในการค้นหาเว็บ, วิเคราะห์โค้ด และจดจำข้อมูลระยะยาว**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
โปรเจกต์นี้คือส่วนขยายของ Sequential Thinking ที่เพิ่มเครื่องมือสำคัญเพื่อให้ AI ทำงานเป็น "Autonomous Agent" ได้อย่างสมบูรณ์แบบ ทั้งการวางแผน (Planning), การหาข้อมูล (Research), และการลงมือทำ (Execution)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- **การให้เหตุผลแบบวนซ้ำ (Iterative Reasoning)**: คิดทีละขั้นตอนอย่างมีโครงสร้าง และปรับปรุงผลลัพธ์ผ่านการวนรอบ
|
|
9
|
-
- **แตกแขนงความคิด (Tree of Thoughts)**: สร้างและประเมินทางเลือกหลายๆ ทาง (เช่น กลยุทธ์แบบระมัดระวัง, แบบสมดุล, หรือแบบกล้าเสี่ยง)
|
|
10
|
-
- **การวิจารณ์ตัวเอง (Self-Critique)**: ตรวจสอบความเสี่ยง ข้อผิดพลาด และอคติในกระบวนการคิดอย่างสม่ำเสมอ
|
|
11
|
-
- **รวมความคิด (Branch Merging)**: สังเคราะห์และรวบรวมข้อมูลเชิงลึกจากเส้นทางการคิดที่แตกต่างกัน
|
|
12
|
-
- **การทดสอบสมมติฐาน (Hypothesis Testing)**: ตั้งสมมติฐานและตรวจสอบความถูกต้องด้วยหลักฐานหรือตรรกะ
|
|
13
|
-
- **ประเมินทางเลือก (Option Evaluation)**: ให้คะแนนและชั่งน้ำหนักทางเลือกต่างๆ (`evaluation`) เพื่อช่วยตัดสินใจ
|
|
14
|
-
- **ทบทวนตัวเอง (Self-Reflexion)**: ย้อนกลับไปแก้ไขความคิดก่อนหน้า (`reflexion`) เพื่อปรับปรุงความแม่นยำ
|
|
15
|
-
- **ปรับเปลี่ยนแบบไดนามิก (Dynamic Adjustment)**: ปรับจำนวนขั้นตอนการคิดเพิ่มหรือลดได้ตามความเหมาะสมของสถานการณ์
|
|
7
|
+
---
|
|
16
8
|
|
|
17
|
-
##
|
|
9
|
+
## 🔑 Environment Variables (การตั้งค่าตัวแปร)
|
|
18
10
|
|
|
19
|
-
|
|
11
|
+
นี่คือตัวแปรทั้งหมดที่ระบบรองรับ คุณสามารถเลือกตั้งค่าได้ตามฟีเจอร์ที่ต้องการใช้งาน:
|
|
20
12
|
|
|
21
|
-
1.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
13
|
+
### 1. Web Search APIs (เลือกอย่างน้อย 1 อย่างเพื่อใช้ `web_search`)
|
|
14
|
+
| Variable Name | Description | Required? |
|
|
15
|
+
|--------------|-------------|-----------|
|
|
16
|
+
| `BRAVE_API_KEY` | สำหรับค้นหาผ่าน Brave Search | ❌ (Optional) |
|
|
17
|
+
| `EXA_API_KEY` | สำหรับค้นหาผ่าน Exa.ai (แนะนำสำหรับงาน Deep Research) | ❌ (Optional) |
|
|
18
|
+
| `GOOGLE_SEARCH_API_KEY` | Google Custom Search API Key | ❌ (Optional) |
|
|
19
|
+
| `GOOGLE_SEARCH_CX` | Google Custom Search Engine ID (CX) | ❌ (Optional) |
|
|
26
20
|
|
|
27
|
-
|
|
21
|
+
### 2. Storage & Memory (การจัดเก็บข้อมูล)
|
|
22
|
+
| Variable Name | Description | Default Value |
|
|
23
|
+
|--------------|-------------|---------------|
|
|
24
|
+
| `THOUGHTS_STORAGE_PATH` | ไฟล์เก็บประวัติการคิด (Thinking History) | `thoughts_history.json` |
|
|
25
|
+
| `NOTES_STORAGE_PATH` | ไฟล์เก็บความจำระยะยาว (Long-term Notes) | `project_notes.json` |
|
|
28
26
|
|
|
29
|
-
###
|
|
27
|
+
### 3. System Behavior (พฤติกรรมระบบ)
|
|
28
|
+
| Variable Name | Description | Default Value |
|
|
29
|
+
|--------------|-------------|---------------|
|
|
30
|
+
| `THOUGHT_DELAY_MS` | เวลาหน่วงระหว่างความคิด (ms) เพื่อกัน Rate Limit | `0` |
|
|
31
|
+
| `DISABLE_THOUGHT_LOGGING`| ปิดการแสดงผล Log สีสวยงามใน Console (`true`/`false`) | `false` |
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
หัวใจหลักสำหรับการแก้ปัญหาอย่างมีโครงสร้าง บังคับให้คิดวิเคราะห์ทีละขั้นตอนก่อนจะสรุปผล
|
|
33
|
+
---
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
- `thought` (string, required): เนื้อหาของความคิดในขั้นตอนนี้
|
|
36
|
-
- `thoughtNumber` (integer, required): ลำดับขั้นตอนปัจจุบัน (เริ่มที่ 1)
|
|
37
|
-
- `totalThoughts` (integer, required): จำนวนขั้นตอนที่คาดว่าจะใช้ (ปรับเปลี่ยนได้ตลอด)
|
|
38
|
-
- `nextThoughtNeeded` (boolean, required): `true` ถ้าต้องคิดต่อ, `false` เมื่อได้คำตอบสุดท้ายแล้ว
|
|
39
|
-
- `thoughtType` (enum): ประเภทของความคิด:
|
|
40
|
-
- `analysis`: วิเคราะห์แยกแยะปัญหา
|
|
41
|
-
- `generation`: ระดมสมองหาทางออก
|
|
42
|
-
- `evaluation`: ประเมินทางเลือก (ใช้คู่กับ `score`)
|
|
43
|
-
- `reflexion`: ทบทวนความคิดก่อนหน้า (ใช้คู่กับ `isRevision`)
|
|
44
|
-
- `selection`: เลือกเส้นทางที่จะไปต่อ
|
|
45
|
-
- `isRevision` (boolean): `true` ถ้ากำลังแก้ไขข้อผิดพลาดก่อนหน้า
|
|
46
|
-
- `revisesThought` (integer): ระบุหมายเลขขั้นตอนที่กำลังแก้ไข
|
|
47
|
-
- `branchFromThought` (integer): หมายเลขขั้นตอนแม่ ถ้าต้องการแตกกิ่งความคิดใหม่
|
|
48
|
-
- `branchId` (string): ชื่อระบุของกิ่งความคิดนั้น (Branch ID)
|
|
35
|
+
## 🚀 Installation & Usage (การติดตั้งและใช้งาน)
|
|
49
36
|
|
|
50
|
-
|
|
37
|
+
สามารถติดตั้งผ่าน npm ได้โดยตรง:
|
|
51
38
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
#### `summarize_history`
|
|
56
|
-
ย่อรวมความคิดหลายๆ ขั้นตอนให้เป็นสรุปเดียว จำเป็นมากสำหรับการคิดที่ยาวนาน เพื่อประหยัด Token
|
|
57
|
-
|
|
58
|
-
**Inputs:**
|
|
59
|
-
- `startIndex` (integer): จุดเริ่มต้นที่จะสรุป
|
|
60
|
-
- `endIndex` (integer): จุดสิ้นสุดที่จะสรุป
|
|
61
|
-
- `summary` (string): ข้อความสรุป
|
|
62
|
-
|
|
63
|
-
### 🌐 ความรู้ภายนอก (External Knowledge)
|
|
64
|
-
|
|
65
|
-
#### `web_search`
|
|
66
|
-
ค้นหาข้อมูลแบบ Real-time จากอินเทอร์เน็ต
|
|
67
|
-
|
|
68
|
-
**Inputs:**
|
|
69
|
-
- `query` (string, required): คำค้นหา
|
|
70
|
-
- `provider` (enum, optional):
|
|
71
|
-
- `brave`: ค้นหาเว็บทั่วไป (ต้องมี `BRAVE_API_KEY`)
|
|
72
|
-
- `exa`: ค้นหาเชิงลึกด้วย AI (ต้องมี `EXA_API_KEY`)
|
|
73
|
-
- `google`: Google Custom Search (ต้องมี `GOOGLE_SEARCH_API_KEY` & `GOOGLE_SEARCH_CX`)
|
|
74
|
-
|
|
75
|
-
#### `fetch`
|
|
76
|
-
ดึงข้อมูล HTTP Request จาก URL โดยตรง มีประโยชน์มากสำหรับดึง HTML, JSON หรือ Text จากแหล่งข้อมูลที่เจอผ่าน Search
|
|
39
|
+
```bash
|
|
40
|
+
npm install @gotza02/sequential-thinking
|
|
41
|
+
```
|
|
77
42
|
|
|
78
|
-
**
|
|
79
|
-
- `url` (string, required): URL เป้าหมาย
|
|
80
|
-
- `method`: `GET` (default), `POST`, `PUT`, `DELETE`
|
|
81
|
-
- `headers`: JSON Object สำหรับ Headers
|
|
82
|
-
- `body`: Request Body สำหรับ POST/PUT
|
|
43
|
+
รองรับทั้ง **Gemini CLI** และ **Claude Desktop**
|
|
83
44
|
|
|
84
|
-
|
|
85
|
-
|
|
45
|
+
### Option A: ใช้งานผ่าน npx (แนะนำสำหรับผู้ใช้ทั่วไป)
|
|
46
|
+
*ไม่ต้องโหลดโค้ด แค่แก้ Config แล้วใช้งานได้เลย*
|
|
86
47
|
|
|
87
|
-
|
|
88
|
-
|
|
48
|
+
#### 1. สำหรับ Gemini CLI
|
|
49
|
+
แก้ไขไฟล์ `~/.gemini/settings.json` (หรือ `config.json`):
|
|
89
50
|
|
|
90
|
-
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"mcpServers": {
|
|
54
|
+
"smartagent": {
|
|
55
|
+
"command": "npx",
|
|
56
|
+
"args": ["-y", "@gotza02/sequential-thinking"],
|
|
57
|
+
"env": {
|
|
58
|
+
"BRAVE_API_KEY": "YOUR_BRAVE_KEY",
|
|
59
|
+
"EXA_API_KEY": "YOUR_EXA_KEY",
|
|
60
|
+
"GOOGLE_SEARCH_API_KEY": "YOUR_GOOGLE_KEY",
|
|
61
|
+
"GOOGLE_SEARCH_CX": "YOUR_GOOGLE_CX",
|
|
62
|
+
"THOUGHTS_STORAGE_PATH": "thoughts_history.json",
|
|
63
|
+
"NOTES_STORAGE_PATH": "project_notes.json",
|
|
64
|
+
"THOUGHT_DELAY_MS": "1000",
|
|
65
|
+
"DISABLE_THOUGHT_LOGGING": "false"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
91
71
|
|
|
92
|
-
####
|
|
93
|
-
|
|
72
|
+
#### 2. สำหรับ Claude Desktop
|
|
73
|
+
แก้ไขไฟล์ `claude_desktop_config.json`:
|
|
94
74
|
|
|
95
|
-
|
|
96
|
-
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"mcpServers": {
|
|
78
|
+
"sequential-thinking": {
|
|
79
|
+
"command": "npx",
|
|
80
|
+
"args": ["-y", "@gotza02/sequential-thinking"],
|
|
81
|
+
"env": {
|
|
82
|
+
"BRAVE_API_KEY": "YOUR_BRAVE_KEY",
|
|
83
|
+
"EXA_API_KEY": "YOUR_EXA_KEY",
|
|
84
|
+
"GOOGLE_SEARCH_API_KEY": "YOUR_GOOGLE_KEY",
|
|
85
|
+
"GOOGLE_SEARCH_CX": "YOUR_GOOGLE_CX",
|
|
86
|
+
"THOUGHTS_STORAGE_PATH": "thoughts_history.json",
|
|
87
|
+
"NOTES_STORAGE_PATH": "project_notes.json",
|
|
88
|
+
"THOUGHT_DELAY_MS": "1000"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
97
94
|
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
### Option B: ใช้งานแบบ Local Source (สำหรับนักพัฒนา)
|
|
98
|
+
*สำหรับคนที่ต้องการแก้ไขโค้ด หรือรันจากเครื่องตัวเอง*
|
|
99
|
+
|
|
100
|
+
1. **Clone & Build:**
|
|
101
|
+
```bash
|
|
102
|
+
git clone <repo_url>
|
|
103
|
+
cd sequential-thinking
|
|
104
|
+
npm install
|
|
105
|
+
npm run build
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
2. **ตั้งค่า Config (ตัวอย่างสำหรับ Gemini CLI):**
|
|
109
|
+
*เปลี่ยน Path ให้ตรงกับที่อยู่ไฟล์ในเครื่องคุณ*
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"mcpServers": {
|
|
114
|
+
"smartagent": {
|
|
115
|
+
"command": "node",
|
|
116
|
+
"args": ["/Users/username/projects/sequential-thinking/dist/index.js"],
|
|
117
|
+
"env": {
|
|
118
|
+
"BRAVE_API_KEY": "YOUR_BRAVE_KEY",
|
|
119
|
+
"EXA_API_KEY": "YOUR_EXA_KEY",
|
|
120
|
+
"GOOGLE_SEARCH_API_KEY": "YOUR_GOOGLE_KEY",
|
|
121
|
+
"GOOGLE_SEARCH_CX": "YOUR_GOOGLE_CX",
|
|
122
|
+
"THOUGHTS_STORAGE_PATH": "thoughts_history.json",
|
|
123
|
+
"NOTES_STORAGE_PATH": "project_notes.json",
|
|
124
|
+
"THOUGHT_DELAY_MS": "1000",
|
|
125
|
+
"DISABLE_THOUGHT_LOGGING": "false"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 🛠️ Tools Capability (ความสามารถเครื่องมือ)
|
|
135
|
+
|
|
136
|
+
### 🧠 1. Cognitive Tools (สมองและการคิด)
|
|
137
|
+
* **`sequentialthinking`**: เครื่องมือหลัก! ช่วยแตกปัญหาใหญ่เป็นขั้นตอนย่อย (Step-by-step), แตกกิ่งความคิด (Branching), และทบทวนตัวเอง (Reflexion)
|
|
138
|
+
* **`summarize_history`**: ย่อสรุปประวัติการคิดที่ยาวเหยียดให้สั้นลง
|
|
139
|
+
* **`clear_thought_history`**: ล้างสมอง เริ่มต้นคิดเรื่องใหม่
|
|
140
|
+
|
|
141
|
+
### 🌐 2. Web Capabilities (การเข้าถึงอินเทอร์เน็ต)
|
|
142
|
+
* **`web_search`**: ค้นหาข้อมูลล่าสุด (รองรับ Brave, Exa, Google)
|
|
143
|
+
* **`read_webpage`**: อ่านเนื้อหาในเว็บแบบ Markdown (ตัดโฆษณาออกให้อัตโนมัติ)
|
|
144
|
+
* **`fetch`**: ดึงข้อมูล Raw JSON/HTML จาก API
|
|
145
|
+
|
|
146
|
+
### 🏗️ 3. Codebase Intelligence (ความเข้าใจโค้ด)
|
|
147
|
+
* **`build_project_graph`**: สแกนโปรเจกต์เพื่อสร้างแผนผังความสัมพันธ์ (Dependency Graph)
|
|
148
|
+
* **`get_file_relationships`**: ดูว่าไฟล์นี้ถูกใครเรียกใช้บ้าง (ป้องกันการแก้แล้วพัง)
|
|
149
|
+
* **`search_code`**: ค้นหาโค้ดทั้งโปรเจกต์แบบฉลาด
|
|
150
|
+
|
|
151
|
+
### 📝 4. Long-term Memory (ความจำระยะยาว)
|
|
152
|
+
* **`manage_notes`**: บันทึก/อ่าน/แก้ไข ข้อความที่ต้องการให้ AI จำได้ข้าม Session (เช่น กฎของโปรเจกต์, ความชอบของผู้ใช้)
|
|
153
|
+
|
|
154
|
+
### 💻 5. System Operations (จัดการระบบ)
|
|
155
|
+
* **`read_file`**: อ่านไฟล์ (ต้องทำก่อนแก้ไฟล์เสมอ)
|
|
156
|
+
* **`edit_file`**: แก้ไขไฟล์เฉพาะจุด (Search & Replace) ปลอดภัยกว่าเขียนทับ
|
|
157
|
+
* **`write_file`**: สร้างไฟล์ใหม่ หรือเขียนทับทั้งไฟล์
|
|
158
|
+
* **`shell_execute`**: รันคำสั่ง Terminal (เช่น `npm test`, `git status`)
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 🤖 Recommended System Instruction (คำสั่งระบบที่แนะนำ)
|
|
163
|
+
|
|
164
|
+
เพื่อประสิทธิภาพสูงสุด ให้ Copy ข้อความด้านล่างนี้ไปใส่ใน **System Prompt** ของ AI Agent ที่คุณใช้งาน:
|
|
100
165
|
|
|
101
|
-
|
|
102
|
-
|
|
166
|
+
```bash
|
|
167
|
+
# 🤖 System Instruction: Enhanced Sequential Engineer
|
|
103
168
|
|
|
104
|
-
|
|
169
|
+
You are an advanced AI Software Engineer enhanced with the **Sequential Thinking MCP Server**. Your goal is to solve complex problems autonomously, accurately, and safely.
|
|
105
170
|
|
|
106
|
-
|
|
107
|
-
อ่านเนื้อหาไฟล์ **ควรอ่านไฟล์ก่อนแก้ไขเสมอ**
|
|
171
|
+
## 🧠 PRIME DIRECTIVE: Sequential Thinking
|
|
108
172
|
|
|
109
|
-
**
|
|
110
|
-
|
|
173
|
+
**Rule #1: NEVER answer complex queries immediately.**
|
|
174
|
+
You MUST use the `sequentialthinking` tool to structure your reasoning process.
|
|
111
175
|
|
|
112
|
-
|
|
113
|
-
|
|
176
|
+
1. **Analyze:** Break the user's request into atomic requirements.
|
|
177
|
+
2. **Plan:** Formulate a step-by-step plan.
|
|
178
|
+
3. **Execute:** Carry out the plan, one step at a time.
|
|
179
|
+
4. **Reflect:** Critique your own work. If a step fails, use `isRevision: true` to correct your course.
|
|
114
180
|
|
|
115
|
-
**
|
|
116
|
-
-
|
|
117
|
-
-
|
|
181
|
+
**Constraints:**
|
|
182
|
+
- Do not output code until you have a clear plan.
|
|
183
|
+
- If you are unsure, generate a hypothesis and verify it.
|
|
118
184
|
|
|
119
|
-
|
|
120
|
-
แก้ไขข้อความบางส่วนในไฟล์ (Search & Replace) เหมาะสำหรับการแก้ไขจุดเล็กๆ โดยไม่ต้องเขียนทับทั้งไฟล์
|
|
185
|
+
## 🏗 Codebase Intelligence Protocol
|
|
121
186
|
|
|
122
|
-
**
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
- `newText` (string, required): ข้อความใหม่
|
|
126
|
-
- `allowMultiple` (boolean, optional): อนุญาตให้เปลี่ยนหลายจุดพร้อมกัน (Default: false)
|
|
187
|
+
**On Start:**
|
|
188
|
+
1. **Map the Territory:** Run `build_project_graph` immediately to understand the project structure.
|
|
189
|
+
2. **Summarize:** Run `get_project_graph_summary` to identify key files.
|
|
127
190
|
|
|
128
|
-
|
|
129
|
-
|
|
191
|
+
**Before Editing:**
|
|
192
|
+
1. **Context Check:** Run `get_file_relationships` to see what depends on the file you are changing.
|
|
193
|
+
2. **Read First:** ALWAYS run `read_file` to get the *current* content. Never rely on memory or assumptions.
|
|
130
194
|
|
|
131
|
-
|
|
132
|
-
- `action` (enum, required): 'add', 'list', 'search', 'update', 'delete'
|
|
133
|
-
- `title` (string): หัวข้อ
|
|
134
|
-
- `content` (string): เนื้อหา
|
|
135
|
-
- `tags` (array): แท็กจัดหมวดหมู่
|
|
136
|
-
- `searchQuery` (string): คำค้นหา (สำหรับ action: search)
|
|
137
|
-
- `noteId` (string): ID ของโน้ต (สำหรับ update/delete)
|
|
195
|
+
## 🛠 File Operations Protocol
|
|
138
196
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
197
|
+
- **Small Changes:** Use `edit_file` for surgical replacements. This minimizes the risk of context loss or accidental deletions.
|
|
198
|
+
- **New/Large Files:** Use `write_file`.
|
|
199
|
+
- **Verification:** After editing, ALWAYS verify the syntax or logic (e.g., run `npm run build` or `npm test` using `shell_execute`).
|
|
142
200
|
|
|
143
|
-
##
|
|
201
|
+
## 🌐 External Knowledge Protocol
|
|
144
202
|
|
|
145
|
-
|
|
146
|
-
- `
|
|
147
|
-
-
|
|
148
|
-
- `THOUGHTS_STORAGE_PATH`: (Optional) Path ของไฟล์ประวัติการคิด (Default: `thoughts_history.json`)
|
|
203
|
+
- **Unknown Libs:** If you see a library you don't know, use `web_search` to find its docs.
|
|
204
|
+
- **Reading:** Use `read_webpage` to ingest documentation efficiently (markdown format).
|
|
205
|
+
- **Errors:** If you hit a cryptic error, search for it.
|
|
149
206
|
|
|
150
|
-
|
|
207
|
+
## 📝 Memory & Persistence
|
|
151
208
|
|
|
152
|
-
|
|
209
|
+
- **Long-Term:** Use `manage_notes` to save architectural decisions, user preferences, or "lessons learned" that should survive this session.
|
|
210
|
+
- **Session:** Your thought process is automatically saved. If you crash or restart, review `thoughts_history.json`.
|
|
153
211
|
|
|
154
|
-
|
|
212
|
+
## 🔄 The Golden Workflow
|
|
155
213
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
"args": [
|
|
162
|
-
"-y",
|
|
163
|
-
"@gotza02/sequential-thinking"
|
|
164
|
-
],
|
|
165
|
-
"env": {
|
|
166
|
-
"BRAVE_API_KEY": "YOUR_BRAVE_API_KEY",
|
|
167
|
-
"EXA_API_KEY": "YOUR_EXA_API_KEY",
|
|
168
|
-
"GOOGLE_SEARCH_API_KEY": "YOUR_GOOGLE_SEARCH_API_KEY",
|
|
169
|
-
"GOOGLE_SEARCH_CX": "YOUR_GOOGLE_SEARCH_CX"
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
214
|
+
1. **Discovery:** `build_project_graph` -> `sequentialthinking` (Plan)
|
|
215
|
+
2. **Research:** `web_search` -> `read_webpage`
|
|
216
|
+
3. **Implementation:** `read_file` -> `sequentialthinking` (Refine) -> `edit_file`
|
|
217
|
+
4. **Verification:** `shell_execute` (Test/Build)
|
|
218
|
+
5. **Completion:** Final answer with summary of changes.
|
|
174
219
|
```
|
|
175
220
|
|
|
176
|
-
|
|
221
|
+
---
|
|
177
222
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
[](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%40gotza02%2Fsequential-thinking%22%5D%7D)
|
|
181
|
-
|
|
182
|
-
## การ Build และ Test
|
|
223
|
+
## 📦 Development
|
|
183
224
|
|
|
184
225
|
```bash
|
|
226
|
+
# Install dependencies
|
|
185
227
|
npm install
|
|
228
|
+
|
|
229
|
+
# Build the project
|
|
186
230
|
npm run build
|
|
187
|
-
```
|
|
188
231
|
|
|
189
|
-
|
|
232
|
+
# Run tests
|
|
190
233
|
npm test
|
|
191
234
|
```
|
|
192
235
|
|
|
193
|
-
## ประวัติการอัปเดต (Recent Updates)
|
|
194
|
-
|
|
195
|
-
### v2026.2.0 (Latest)
|
|
196
|
-
- **Refactoring**: ปรับโครงสร้างโค้ดครั้งใหญ่ แยกการทำงานเป็นโมดูล (`src/tools/`) เพื่อให้อ่านง่ายและรองรับการขยายตัวในอนาคต
|
|
197
|
-
|
|
198
|
-
### v2026.1.31
|
|
199
|
-
- **New Tools**:
|
|
200
|
-
- `manage_notes`: ระบบความจำระยะยาวสำหรับเก็บข้อมูลข้าม Session
|
|
201
|
-
|
|
202
|
-
### v2026.1.30
|
|
203
|
-
- **New Tools**:
|
|
204
|
-
- `get_project_graph_visualization`: สร้าง Mermaid Diagram แสดงโครงสร้างโปรเจกต์
|
|
205
|
-
|
|
206
|
-
### v2026.1.29
|
|
207
|
-
- **New Tools**:
|
|
208
|
-
- `edit_file`: แก้ไขไฟล์เฉพาะจุดโดยไม่ต้องเขียนทับทั้งหมด
|
|
209
|
-
|
|
210
|
-
### v2026.1.28
|
|
211
|
-
- **Robustness**:
|
|
212
|
-
- เพิ่ม **Atomic Writes** สำหรับ `thoughts_history.json` ป้องกันไฟล์เสีย
|
|
213
|
-
- เพิ่ม **API Retry Logic** (Exponential Backoff) สำหรับเครื่องมือ Web Search ทั้งหมด
|
|
214
|
-
- **New Tools**:
|
|
215
|
-
- `summarize_history`: ย่อประวัติการคิด
|
|
216
|
-
|
|
217
|
-
### v2026.1.27
|
|
218
|
-
- **New Tools**:
|
|
219
|
-
- `read_webpage`: อ่านหน้าเว็บเป็น Markdown
|
|
220
|
-
- `search_code`: ค้นหาข้อความในไฟล์โค้ดแบบ Recursive
|
|
221
|
-
- `clear_thought_history`: ล้างประวัติการคิด
|
|
222
|
-
|
|
223
236
|
## License
|
|
224
|
-
|
|
225
|
-
MIT License
|
|
237
|
+
MIT
|
package/dist/tools/web.js
CHANGED
|
@@ -9,71 +9,90 @@ export function registerWebTools(server) {
|
|
|
9
9
|
query: z.string().min(1).describe("The search query"),
|
|
10
10
|
provider: z.enum(['brave', 'exa', 'google']).optional().describe("Preferred search provider")
|
|
11
11
|
}, async ({ query, provider }) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
12
|
+
const errors = [];
|
|
13
|
+
// 1. Identify available providers
|
|
14
|
+
const availableProviders = [];
|
|
15
|
+
if (process.env.BRAVE_API_KEY)
|
|
16
|
+
availableProviders.push('brave');
|
|
17
|
+
if (process.env.EXA_API_KEY)
|
|
18
|
+
availableProviders.push('exa');
|
|
19
|
+
if (process.env.GOOGLE_SEARCH_API_KEY && process.env.GOOGLE_SEARCH_CX)
|
|
20
|
+
availableProviders.push('google');
|
|
21
|
+
if (availableProviders.length === 0) {
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: "text", text: "Error: No search provider configured. Please set BRAVE_API_KEY, EXA_API_KEY, or GOOGLE_SEARCH_API_KEY." }],
|
|
24
|
+
isError: true
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
// 2. Determine execution order
|
|
28
|
+
let attemptOrder = [...availableProviders];
|
|
29
|
+
if (provider) {
|
|
30
|
+
if (availableProviders.includes(provider)) {
|
|
31
|
+
// Move requested provider to the front
|
|
32
|
+
attemptOrder = [provider, ...availableProviders.filter(p => p !== provider)];
|
|
24
33
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
});
|
|
31
|
-
if (!response.ok)
|
|
32
|
-
throw new Error(`Brave API error: ${response.statusText}`);
|
|
33
|
-
const data = await response.json();
|
|
34
|
-
return { content: [{ type: "text", text: JSON.stringify(data.web?.results || data, null, 2) }] };
|
|
34
|
+
else {
|
|
35
|
+
return {
|
|
36
|
+
content: [{ type: "text", text: `Error: Requested provider '${provider}' is not configured or unavailable.` }],
|
|
37
|
+
isError: true
|
|
38
|
+
};
|
|
35
39
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
}
|
|
41
|
+
// Default priority if no preference: Brave > Exa > Google (Already respected by push order if mapped correctly, but let's be explicit if needed. Here they are added in that order.)
|
|
42
|
+
// 3. Try providers sequentially
|
|
43
|
+
for (const currentProvider of attemptOrder) {
|
|
44
|
+
try {
|
|
45
|
+
if (currentProvider === 'brave') {
|
|
46
|
+
const response = await fetchWithRetry(`https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(query)}&count=5`, {
|
|
47
|
+
headers: {
|
|
48
|
+
'X-Subscription-Token': process.env.BRAVE_API_KEY,
|
|
49
|
+
'Accept': 'application/json'
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
if (!response.ok)
|
|
53
|
+
throw new Error(`Brave API error: ${response.status} ${response.statusText}`);
|
|
54
|
+
const data = await response.json();
|
|
55
|
+
return { content: [{ type: "text", text: JSON.stringify(data.web?.results || data, null, 2) }] };
|
|
56
|
+
}
|
|
57
|
+
if (currentProvider === 'exa') {
|
|
58
|
+
const response = await fetchWithRetry('https://api.exa.ai/search', {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: {
|
|
61
|
+
'x-api-key': process.env.EXA_API_KEY,
|
|
62
|
+
'Content-Type': 'application/json'
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify({ query, numResults: 5 })
|
|
65
|
+
});
|
|
66
|
+
if (!response.ok)
|
|
67
|
+
throw new Error(`Exa API error: ${response.status} ${response.statusText}`);
|
|
68
|
+
const data = await response.json();
|
|
69
|
+
return { content: [{ type: "text", text: JSON.stringify(data.results || data, null, 2) }] };
|
|
70
|
+
}
|
|
71
|
+
if (currentProvider === 'google') {
|
|
72
|
+
const response = await fetchWithRetry(`https://www.googleapis.com/customsearch/v1?key=${process.env.GOOGLE_SEARCH_API_KEY}&cx=${process.env.GOOGLE_SEARCH_CX}&q=${encodeURIComponent(query)}&num=5`);
|
|
73
|
+
if (!response.ok)
|
|
74
|
+
throw new Error(`Google API error: ${response.status} ${response.statusText}`);
|
|
75
|
+
const data = await response.json();
|
|
76
|
+
// Extract relevant fields to keep output clean
|
|
77
|
+
const results = data.items?.map((item) => ({
|
|
78
|
+
title: item.title,
|
|
79
|
+
link: item.link,
|
|
80
|
+
snippet: item.snippet
|
|
81
|
+
})) || [];
|
|
82
|
+
return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
|
|
83
|
+
}
|
|
51
84
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
throw new Error("GOOGLE_SEARCH_CX (Search Engine ID) not found");
|
|
57
|
-
const response = await fetchWithRetry(`https://www.googleapis.com/customsearch/v1?key=${process.env.GOOGLE_SEARCH_API_KEY}&cx=${process.env.GOOGLE_SEARCH_CX}&q=${encodeURIComponent(query)}&num=5`);
|
|
58
|
-
if (!response.ok)
|
|
59
|
-
throw new Error(`Google API error: ${response.statusText}`);
|
|
60
|
-
const data = await response.json();
|
|
61
|
-
// Extract relevant fields to keep output clean
|
|
62
|
-
const results = data.items?.map((item) => ({
|
|
63
|
-
title: item.title,
|
|
64
|
-
link: item.link,
|
|
65
|
-
snippet: item.snippet
|
|
66
|
-
})) || [];
|
|
67
|
-
return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
|
|
85
|
+
catch (error) {
|
|
86
|
+
const errorMsg = `[${currentProvider}] ${error instanceof Error ? error.message : String(error)}`;
|
|
87
|
+
errors.push(errorMsg);
|
|
88
|
+
// Continue to next provider
|
|
68
89
|
}
|
|
69
|
-
return { content: [{ type: "text", text: "Error: Unsupported or unconfigured provider." }], isError: true };
|
|
70
|
-
}
|
|
71
|
-
catch (error) {
|
|
72
|
-
return {
|
|
73
|
-
content: [{ type: "text", text: `Search Error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
74
|
-
isError: true
|
|
75
|
-
};
|
|
76
90
|
}
|
|
91
|
+
// 4. All failed
|
|
92
|
+
return {
|
|
93
|
+
content: [{ type: "text", text: `All search providers failed:\n${errors.join('\n')}` }],
|
|
94
|
+
isError: true
|
|
95
|
+
};
|
|
77
96
|
});
|
|
78
97
|
// 2. fetch
|
|
79
98
|
server.tool("fetch", "Perform an HTTP request to a specific URL.", {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { registerWebTools } from './tools/web.js';
|
|
3
|
+
import * as utils from './utils.js';
|
|
4
|
+
// Mock utils
|
|
5
|
+
vi.mock('./utils.js', async (importOriginal) => {
|
|
6
|
+
const actual = await importOriginal();
|
|
7
|
+
return {
|
|
8
|
+
...actual,
|
|
9
|
+
fetchWithRetry: vi.fn(),
|
|
10
|
+
};
|
|
11
|
+
});
|
|
12
|
+
describe('web_search fallback', () => {
|
|
13
|
+
let mockToolCallback;
|
|
14
|
+
const mockServer = {
|
|
15
|
+
tool: vi.fn((name, desc, schema, callback) => {
|
|
16
|
+
if (name === 'web_search') {
|
|
17
|
+
mockToolCallback = callback;
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
};
|
|
21
|
+
const originalEnv = process.env;
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
process.env = { ...originalEnv };
|
|
24
|
+
vi.clearAllMocks();
|
|
25
|
+
});
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
process.env = originalEnv;
|
|
28
|
+
});
|
|
29
|
+
it('should use Brave if configured and no provider specified', async () => {
|
|
30
|
+
process.env.BRAVE_API_KEY = 'test-brave-key';
|
|
31
|
+
delete process.env.EXA_API_KEY;
|
|
32
|
+
delete process.env.GOOGLE_SEARCH_API_KEY;
|
|
33
|
+
registerWebTools(mockServer);
|
|
34
|
+
const mockResponse = {
|
|
35
|
+
ok: true,
|
|
36
|
+
json: async () => ({ web: { results: ['brave result'] } })
|
|
37
|
+
};
|
|
38
|
+
utils.fetchWithRetry.mockResolvedValue(mockResponse);
|
|
39
|
+
const result = await mockToolCallback({ query: 'test' });
|
|
40
|
+
expect(utils.fetchWithRetry).toHaveBeenCalledWith(expect.stringContaining('api.search.brave.com'), expect.anything());
|
|
41
|
+
expect(result.isError).toBeUndefined();
|
|
42
|
+
expect(JSON.parse(result.content[0].text)).toEqual(['brave result']);
|
|
43
|
+
});
|
|
44
|
+
it('should fallback to Exa if Brave fails', async () => {
|
|
45
|
+
process.env.BRAVE_API_KEY = 'test-brave-key';
|
|
46
|
+
process.env.EXA_API_KEY = 'test-exa-key';
|
|
47
|
+
registerWebTools(mockServer);
|
|
48
|
+
// First call (Brave) fails
|
|
49
|
+
utils.fetchWithRetry
|
|
50
|
+
.mockResolvedValueOnce({ ok: false, statusText: 'Brave Error', status: 500 })
|
|
51
|
+
// Second call (Exa) succeeds
|
|
52
|
+
.mockResolvedValueOnce({
|
|
53
|
+
ok: true,
|
|
54
|
+
json: async () => ({ results: ['exa result'] })
|
|
55
|
+
});
|
|
56
|
+
const result = await mockToolCallback({ query: 'test' });
|
|
57
|
+
expect(utils.fetchWithRetry).toHaveBeenCalledTimes(2);
|
|
58
|
+
// 1. Brave
|
|
59
|
+
expect(utils.fetchWithRetry).toHaveBeenNthCalledWith(1, expect.stringContaining('api.search.brave.com'), expect.anything());
|
|
60
|
+
// 2. Exa
|
|
61
|
+
expect(utils.fetchWithRetry).toHaveBeenNthCalledWith(2, expect.stringContaining('api.exa.ai'), expect.anything());
|
|
62
|
+
expect(result.isError).toBeUndefined();
|
|
63
|
+
expect(JSON.parse(result.content[0].text)).toEqual(['exa result']);
|
|
64
|
+
});
|
|
65
|
+
it('should respect requested provider and verify its availability', async () => {
|
|
66
|
+
process.env.BRAVE_API_KEY = 'test-brave-key';
|
|
67
|
+
// Exa not configured
|
|
68
|
+
delete process.env.EXA_API_KEY;
|
|
69
|
+
registerWebTools(mockServer);
|
|
70
|
+
const result = await mockToolCallback({ query: 'test', provider: 'exa' });
|
|
71
|
+
expect(result.isError).toBe(true);
|
|
72
|
+
expect(result.content[0].text).toContain("Requested provider 'exa' is not configured");
|
|
73
|
+
});
|
|
74
|
+
it('should try requested provider first, then fallback', async () => {
|
|
75
|
+
process.env.BRAVE_API_KEY = 'test-brave-key';
|
|
76
|
+
process.env.EXA_API_KEY = 'test-exa-key';
|
|
77
|
+
registerWebTools(mockServer);
|
|
78
|
+
// Request Exa
|
|
79
|
+
// Mock Exa fail, Brave success
|
|
80
|
+
utils.fetchWithRetry
|
|
81
|
+
.mockResolvedValueOnce({ ok: false, statusText: 'Exa Error', status: 500 })
|
|
82
|
+
.mockResolvedValueOnce({
|
|
83
|
+
ok: true,
|
|
84
|
+
json: async () => ({ web: { results: ['brave result'] } })
|
|
85
|
+
});
|
|
86
|
+
const result = await mockToolCallback({ query: 'test', provider: 'exa' });
|
|
87
|
+
expect(utils.fetchWithRetry).toHaveBeenCalledTimes(2);
|
|
88
|
+
// 1. Exa (requested)
|
|
89
|
+
expect(utils.fetchWithRetry).toHaveBeenNthCalledWith(1, expect.stringContaining('api.exa.ai'), expect.anything());
|
|
90
|
+
// 2. Brave (fallback)
|
|
91
|
+
expect(utils.fetchWithRetry).toHaveBeenNthCalledWith(2, expect.stringContaining('api.search.brave.com'), expect.anything());
|
|
92
|
+
expect(result.isError).toBeUndefined();
|
|
93
|
+
});
|
|
94
|
+
it('should return error if all fail', async () => {
|
|
95
|
+
process.env.BRAVE_API_KEY = 'test-brave-key';
|
|
96
|
+
registerWebTools(mockServer);
|
|
97
|
+
utils.fetchWithRetry.mockResolvedValue({ ok: false, statusText: 'Some Error', status: 500 });
|
|
98
|
+
const result = await mockToolCallback({ query: 'test' });
|
|
99
|
+
expect(result.isError).toBe(true);
|
|
100
|
+
expect(result.content[0].text).toContain("All search providers failed");
|
|
101
|
+
expect(result.content[0].text).toContain("Brave API error");
|
|
102
|
+
});
|
|
103
|
+
});
|