sp-rag 0.6.7 → 0.6.9

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 CHANGED
@@ -1,275 +1,301 @@
1
- # `sp-rag`
2
-
3
- CLI để setup nhanh SP-RAG theo hướng dev-friendly:
4
-
5
- - lưu cấu hình mặc định để dev không phải nhớ lại URL, client, alias
6
- - cài MCP config đúng format cho từng client
7
- - cài native skill / rule / custom agent cho từng IDE khi có convention ổn định
8
- - kiểm tra nhanh health và observability của stack
9
- - gọi sync codegraph/GitNexus theo branch hoặc `commit_sha`
10
- - đọc docs đã render
11
- - chạy evaluation/regression suite từ file JSON
12
-
13
- ## Yêu cầu
14
-
15
- - Node.js `>= 20`
16
-
17
- ## Trạng thái package
18
-
19
- - package npm public: `sp-rag`
20
- - version đang publish: `0.6.7`
21
- - binary public: `sp-rag`
22
-
23
- ## Cài từ source trong monorepo
24
-
25
- ```bash
26
- cd apps/sp-rag-cli
27
- npm install
28
- npm test -- --run
29
- npm run build
30
- node dist/index.js doctor
31
- ```
32
-
33
- ## Cài nhanh qua `npx`
34
-
35
- ```bash
36
- npx sp-rag@latest install --client codex --mcp-token <grc_pat_...> --doctor
37
- npx sp-rag@latest install --client cursor --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
38
- npx sp-rag@latest install --client vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
39
- npx sp-rag@latest add --client claude-code --scope project --cwd D:/Webs/seo-booster
40
- npx sp-rag@latest explain --client vscode --scope project --cwd D:/Webs/seo-booster
41
- npx sp-rag@latest token add --token <grc_pat_...>
42
- npx sp-rag@latest token verify --token <grc_pat_...>
43
- npx sp-rag@latest mcp add antigravity
44
- npx sp-rag@latest mcp add opencode --scope project --cwd D:/Webs/seo-booster
45
- npx sp-rag@latest skill install --client cursor --scope project --cwd D:/Webs/seo-booster
46
- npx sp-rag@latest skill install --client vscode --scope project --cwd D:/Webs/seo-booster
47
- ```
48
-
49
- Tương đương bằng `npm`:
50
-
51
- ```bash
52
- npm exec --yes sp-rag@latest install -- --client codex --mcp-token <grc_pat_...> --doctor
53
- npm exec --yes sp-rag@latest add -- --client claude-code --scope project --cwd D:/Webs/seo-booster
54
- npm exec --yes sp-rag@latest explain -- --client vscode --scope project --cwd D:/Webs/seo-booster
55
- npm exec --yes sp-rag@latest token add -- --token <grc_pat_...>
56
- npm exec --yes sp-rag@latest token verify -- --token <grc_pat_...>
57
- ```
58
-
59
- ## Flow gọn cho dev
60
-
61
- Flow khuyên dùng sau khi đã có `grc_pat_*`:
62
-
63
- 1. chạy `install` đúng một lần cho client đầu tiên, có kèm `--mcp-token`
64
- 2. từ lần sau, dùng `add --client ...` để cài thêm MCP + skill cho client khác mà không phải nhập lại token
65
- 3. khi chỉ muốn làm một nửa, dùng `mcp add` hoặc `skill install`
66
- 4. khi đổi token, chỉ cần `token add`
67
- 5. khi muốn kiểm tra máy đang được cấu hình ra sao, dùng `explain`
68
-
69
- Ghi chú:
70
-
71
- - với `cursor` và `vscode` ở `scope project`, nếu Sếpp đang đứng sẵn trong repo thì có thể bỏ `--cwd`
72
- - CLI sẽ tự dùng thư mục hiện tại cho cả MCP lẫn skill
73
- - rule/agent mới đã được tăng độ ưu tiên MCP-first, giảm khả năng model nhảy thẳng sang grep/read file local
74
- - skill mới cũng dặn model tổng hợp từ `matched_passages`, `top_entities`, `top_relations`, `citations`; không bê nguyên `answer_brief`
75
-
76
- Ví dụ:
77
-
78
- ```bash
79
- sp-rag install --client vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...> --doctor
80
- sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
81
- sp-rag mcp add antigravity
82
- sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
83
- sp-rag token add --token <grc_pat_moi>
84
- sp-rag explain --client vscode --scope project --cwd D:/Webs/seo-booster
85
- ```
86
-
87
- ## Lấy token ở đâu
88
-
89
- `sp-rag` public bây giờ dùng token gắn với tài khoản thật trên server.
90
-
91
- Luồng chuẩn:
92
-
93
- 1. đăng nhập dashboard hoặc API account của GraphRAG
94
- 2. tạo `personal access token` qua account hiện tại
95
- 3. dùng chính token `grc_pat_*` đó cho MCP và CLI
96
- 4. kiểm tra token bằng `sp-rag token verify`
97
-
98
- Điểm quan trọng:
99
-
100
- - token dev dùng cho MCP nên là `grc_pat_*` của account
101
- - quyền nhìn thấy tool/resource trên MCP bám theo role của account đó
102
- - `viewer`, `operator`, `approver`, `admin` sẽ thấy inventory khác nhau
103
- - `MCP_SERVER_ACCESS_TOKENS_JSON` chỉ còn là fallback legacy/break-glass, không còn là luồng chính cho dev
104
- - khi client đã có sẵn một alias trỏ cùng MCP endpoint, CLI sẽ ưu tiên cập nhật alias đó thay vì tạo alias mới song song
105
-
106
- Tự tạo PAT bằng API hiện có:
107
-
108
- 1. lấy session token qua `POST /v1/account/login`
109
- 2. gọi `POST /v1/account/personal-access-tokens`
110
- 3. dùng trường `generated_token`
111
-
112
- Ví dụ đầy đủ:
113
-
114
- ```bash
115
- curl -X POST https://sp-rag.secomapp.com/api/v1/account/login \
116
- -H "Content-Type: application/json" \
117
- -d '{"email":"dev@example.com","password":"<mật-khẩu>"}'
118
- ```
119
-
120
- Lấy `session_token`, rồi gọi tiếp:
121
-
122
- ```bash
123
- curl -X POST https://sp-rag.secomapp.com/api/v1/account/personal-access-tokens \
124
- -H "Authorization: Bearer <grc_sess_...>" \
125
- -H "Content-Type: application/json" \
126
- -d '{"label":"SP-RAG CLI","expires_in_days":30}'
127
- ```
128
-
129
- Response sẽ trả `generated_token`, chính là token `grc_pat_*` để đưa vào `sp-rag`.
130
-
131
- Ví dụ dev dùng token trực tiếp:
132
-
133
- ```bash
134
- npx sp-rag@latest install --client cursor --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
135
- npx sp-rag@latest token verify --token <grc_pat_...>
136
- npx sp-rag@latest explain --client cursor --scope project --cwd D:/Webs/seo-booster
137
- ```
138
-
139
- Hoặc dùng biến môi trường:
140
-
141
- ```bash
142
- $env:GRAPHRAG_MCP_TOKEN="<grc_pat_...>"
143
- npx sp-rag@latest mcp add vscode --scope project --cwd D:/Webs/seo-booster --auth-env-var GRAPHRAG_MCP_TOKEN
144
- npx sp-rag@latest token verify --token $env:GRAPHRAG_MCP_TOKEN
145
- ```
146
-
147
- ## MCP client được hỗ trợ
148
-
149
- - `codex`
150
- - `cursor`
151
- - `claude-code`
152
- - `antigravity`
153
- - `vscode`
154
- - `opencode`
155
-
156
- ## Skill client được hỗ trợ
157
-
158
- - `codex` -> `SKILL.md`
159
- - `claude-code` -> `SKILL.md`
160
- - `antigravity` -> `SKILL.md`
161
- - `opencode` -> `SKILL.md`
162
- - `cursor` -> `.cursor/rules/sp-rag.mdc`
163
- - `vscode` -> `.github/agents/sp-rag.agent.md` + `.github/copilot-instructions.md` hoặc `~/.copilot/agents/sp-rag.agent.md` + `~/.copilot/instructions/sp-rag.instructions.md`
164
-
165
- Ghi chú:
166
-
167
- - generated skill luôn được render bằng tiếng Anh
168
- - `cursor` hiện nên dùng `scope project` cho rule `.mdc`
169
- - `vscode` hỗ trợ cả `scope project` lẫn `scope global`
170
- - nếu không muốn lưu token literal vào file config client, dùng `sp-rag mcp add --auth-env-var SP_RAG_MCP_TOKEN`
171
-
172
- ## Path mặc định quan trọng
173
-
174
- ### MCP
175
-
176
- - `codex`: `~/.codex/config.toml`
177
- - `cursor` project: `.cursor/mcp.json`
178
- - `cursor` global: `~/.cursor/mcp.json`
179
- - `claude-code`: `.mcp.json`
180
- - `antigravity`: `~/.gemini/antigravity/mcp_config.json`
181
- - `vscode` project: `.vscode/mcp.json`
182
- - `vscode` global: file `mcp.json` trong user profile của VS Code
183
- - `opencode` project: `opencode.json`
184
- - `opencode` global: `~/.config/opencode/opencode.json`
185
-
186
- ### Skill / rule / custom agent
187
-
188
- - `codex`: `~/.codex/skills/sp-rag/SKILL.md`
189
- - `claude-code`: `~/.claude/skills/sp-rag/SKILL.md`
190
- - `antigravity`: `~/.gemini/antigravity/skills/sp-rag/SKILL.md`
191
- - `opencode`: `~/.config/opencode/skills/sp-rag/SKILL.md`
192
- - `cursor` project: `.cursor/rules/sp-rag.mdc`
193
- - `vscode` project: `.github/agents/sp-rag.agent.md` và `.github/copilot-instructions.md`
194
- - `vscode` global: `~/.copilot/agents/sp-rag.agent.md` và `~/.copilot/instructions/sp-rag.instructions.md`
195
-
196
- ## Luồng khuyên dùng cho dev mới
197
-
198
- ```bash
199
- sp-rag install --client codex --mcp-token <grc_pat_...> --doctor
200
- sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
201
- sp-rag explain --client codex
202
- sp-rag token add --token <grc_pat_...>
203
- sp-rag token verify --token <grc_pat_...>
204
- sp-rag config show
205
- sp-rag codegraph status
206
- sp-rag codegraph watch --interval-ms 2000
207
- sp-rag codegraph runs --limit 5
208
- sp-rag codegraph metrics
209
- sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
210
- sp-rag mcp add antigravity
211
- sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
212
- sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
213
- sp-rag skill install --client codex
214
- sp-rag skill install --client cursor --scope project --cwd D:/Webs/seo-booster
215
- sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
216
- sp-rag eval run --file ./examples/eval-suite.sample.json
217
- ```
218
-
219
- ## Lệnh chính
220
-
221
- ```bash
222
- sp-rag install --client codex --mcp-token <grc_pat_...> --doctor
223
- sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
224
- sp-rag explain --client codex
225
- sp-rag token add --token <grc_pat_...>
226
- sp-rag token verify --token <grc_pat_...>
227
- sp-rag config show
228
- sp-rag doctor
229
- sp-rag codegraph status
230
- sp-rag codegraph watch --interval-ms 2000
231
- sp-rag codegraph runs --limit 10
232
- sp-rag codegraph metrics
233
- sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
234
- sp-rag codegraph sync --branch master --commit-sha <sha> --webhook-token <token webhook codegraph> --gitlab-job-token <ci-job-token>
235
- sp-rag docs get public --format md
236
- sp-rag mcp add codex
237
- sp-rag mcp add antigravity
238
- sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
239
- sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
240
- sp-rag skill install --client codex
241
- sp-rag skill install --client cursor --scope project --cwd D:/Webs/seo-booster
242
- sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
243
- sp-rag eval run --file ./examples/eval-suite.sample.json
244
- sp-rag update setup --client codex
245
- ```
246
-
247
- ## Cấu hình mặc định
248
-
249
- CLI lưu cấu hình tại:
250
-
251
- - `~/.sp-rag/config.json`
252
- - thể override home dir bằng `SP_RAG_HOME_DIR`
253
-
254
- CLI thể lưu thêm:
255
-
256
- - `mcpToken` cho flow cài nhanh
257
- - `authEnvVar` nếu muốn client đọc token từ biến môi trường thay vì lưu literal
258
- - `defaultClient`, `defaultScope`, `skillClient`
259
-
260
- Giá trị mặc định:
261
-
262
- - base URL: `https://sp-rag.secomapp.com`
263
- - MCP URL: `https://sp-rag.secomapp.com/mcp`
264
- - alias MCP: `sp-rag`
265
-
266
- ## Evaluation mẫu
267
-
268
- - file mẫu: [`examples/eval-suite.sample.json`](./examples/eval-suite.sample.json)
269
-
270
- ## Tài liệu thêm
271
-
272
- - [Hướng dẫn dev sử dụng SP-RAG](../../docs/runbooks/dev-usage-guide.md)
273
- - [Runbook CLI `sp-rag`](../../docs/runbooks/sp-rag-cli.md)
274
- - [Runbook phát hành CLI `sp-rag`](../../docs/runbooks/sp-rag-cli-release.md)
275
- - [Runbook MCP Public](../../docs/runbooks/mcp-public-clients.md)
1
+ # `sp-rag`
2
+
3
+ CLI để setup nhanh SP-RAG theo hướng dev-friendly:
4
+
5
+ - lưu cấu hình mặc định để dev không phải nhớ lại URL, client, alias
6
+ - cài MCP config đúng format cho từng client
7
+ - cài native skill / rule / custom agent cho từng IDE khi có convention ổn định
8
+ - kiểm tra nhanh health và observability của stack
9
+ - gọi sync codegraph/GitNexus theo branch hoặc `commit_sha`
10
+ - đọc docs đã render
11
+ - chạy evaluation/regression suite từ file JSON
12
+
13
+ ## Yêu cầu
14
+
15
+ - Node.js `>= 20`
16
+
17
+ ## Trạng thái package
18
+
19
+ - package npm public: `sp-rag`
20
+ - version đang publish: `0.6.7`
21
+ - binary public: `sp-rag`
22
+
23
+ ## Cài từ source trong monorepo
24
+
25
+ ```bash
26
+ cd apps/sp-rag-cli
27
+ npm install
28
+ npm test -- --run
29
+ npm run build
30
+ node dist/index.js doctor
31
+ ```
32
+
33
+ ## Cài nhanh qua `npx`
34
+
35
+ ```bash
36
+ npx sp-rag@latest install --client codex --mcp-token <grc_pat_...> --doctor
37
+ npx sp-rag@latest install --client cursor --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
38
+ npx sp-rag@latest install --client vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
39
+ npx sp-rag@latest add --client claude-code --scope project --cwd D:/Webs/seo-booster
40
+ npx sp-rag@latest explain --client vscode --scope project --cwd D:/Webs/seo-booster
41
+ npx sp-rag@latest token add --token <grc_pat_...>
42
+ npx sp-rag@latest token verify --token <grc_pat_...>
43
+ npx sp-rag@latest mcp add antigravity
44
+ npx sp-rag@latest mcp add opencode --scope project --cwd D:/Webs/seo-booster
45
+ npx sp-rag@latest skill install --client cursor --scope project --cwd D:/Webs/seo-booster
46
+ npx sp-rag@latest skill install --client vscode --scope project --cwd D:/Webs/seo-booster
47
+ ```
48
+
49
+ Tương đương bằng `npm`:
50
+
51
+ ```bash
52
+ npm exec --yes sp-rag@latest install -- --client codex --mcp-token <grc_pat_...> --doctor
53
+ npm exec --yes sp-rag@latest add -- --client claude-code --scope project --cwd D:/Webs/seo-booster
54
+ npm exec --yes sp-rag@latest explain -- --client vscode --scope project --cwd D:/Webs/seo-booster
55
+ npm exec --yes sp-rag@latest token add -- --token <grc_pat_...>
56
+ npm exec --yes sp-rag@latest token verify -- --token <grc_pat_...>
57
+ ```
58
+
59
+ ## Flow gọn cho dev
60
+
61
+ Flow khuyên dùng sau khi đã có `grc_pat_*`:
62
+
63
+ 1. chạy `install` đúng một lần cho client đầu tiên, có kèm `--mcp-token`
64
+ 2. từ lần sau, dùng `add --client ...` để cài thêm MCP + skill cho client khác mà không phải nhập lại token
65
+ 3. khi chỉ muốn làm một nửa, dùng `mcp add` hoặc `skill install`
66
+ 4. khi đổi token, chỉ cần `token add`
67
+ 5. khi muốn kiểm tra máy đang được cấu hình ra sao, dùng `explain`
68
+
69
+ Ghi chú:
70
+
71
+ - với `cursor` và `vscode` ở `scope project`, nếu Sếpp đang đứng sẵn trong repo thì có thể bỏ `--cwd`
72
+ - CLI sẽ tự dùng thư mục hiện tại cho cả MCP lẫn skill
73
+ - rule/agent mới đã được tăng độ ưu tiên MCP-first, giảm khả năng model nhảy thẳng sang grep/read file local
74
+ - skill mới cũng dặn model tổng hợp từ `matched_passages`, `top_entities`, `top_relations`, `citations`; không bê nguyên `answer_brief`
75
+
76
+ Ví dụ:
77
+
78
+ ```bash
79
+ sp-rag install --client vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...> --doctor
80
+ sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
81
+ sp-rag mcp add antigravity
82
+ sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
83
+ sp-rag token add --token <grc_pat_moi>
84
+ sp-rag explain --client vscode --scope project --cwd D:/Webs/seo-booster
85
+ ```
86
+
87
+ ## Lấy token ở đâu
88
+
89
+ `sp-rag` public bây giờ dùng token gắn với tài khoản thật trên server.
90
+
91
+ Luồng chuẩn:
92
+
93
+ 1. đăng nhập dashboard hoặc API account của GraphRAG
94
+ 2. tạo `personal access token` qua account hiện tại
95
+ 3. dùng chính token `grc_pat_*` đó cho MCP và CLI
96
+ 4. kiểm tra token bằng `sp-rag token verify`
97
+
98
+ Điểm quan trọng:
99
+
100
+ - token dev dùng cho MCP nên là `grc_pat_*` của account
101
+ - quyền nhìn thấy tool/resource trên MCP bám theo role của account đó
102
+ - `viewer`, `operator`, `approver`, `admin` sẽ thấy inventory khác nhau
103
+ - `MCP_SERVER_ACCESS_TOKENS_JSON` chỉ còn là fallback legacy/break-glass, không còn là luồng chính cho dev
104
+ - khi client đã có sẵn một alias trỏ cùng MCP endpoint, CLI sẽ ưu tiên cập nhật alias đó thay vì tạo alias mới song song
105
+
106
+ Tự tạo PAT bằng API hiện có:
107
+
108
+ 1. lấy session token qua `POST /v1/account/login`
109
+ 2. gọi `POST /v1/account/personal-access-tokens`
110
+ 3. dùng trường `generated_token`
111
+
112
+ Ví dụ đầy đủ:
113
+
114
+ ```bash
115
+ curl -X POST https://sp-rag.secomapp.com/api/v1/account/login \
116
+ -H "Content-Type: application/json" \
117
+ -d '{"email":"dev@example.com","password":"<mật-khẩu>"}'
118
+ ```
119
+
120
+ Lấy `session_token`, rồi gọi tiếp:
121
+
122
+ ```bash
123
+ curl -X POST https://sp-rag.secomapp.com/api/v1/account/personal-access-tokens \
124
+ -H "Authorization: Bearer <grc_sess_...>" \
125
+ -H "Content-Type: application/json" \
126
+ -d '{"label":"SP-RAG CLI","expires_in_days":30}'
127
+ ```
128
+
129
+ Response sẽ trả `generated_token`, chính là token `grc_pat_*` để đưa vào `sp-rag`.
130
+
131
+ Ví dụ dev dùng token trực tiếp:
132
+
133
+ ```bash
134
+ npx sp-rag@latest install --client cursor --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
135
+ npx sp-rag@latest token verify --token <grc_pat_...>
136
+ npx sp-rag@latest explain --client cursor --scope project --cwd D:/Webs/seo-booster
137
+ ```
138
+
139
+ Hoặc dùng biến môi trường:
140
+
141
+ ```bash
142
+ $env:GRAPHRAG_MCP_TOKEN="<grc_pat_...>"
143
+ npx sp-rag@latest mcp add vscode --scope project --cwd D:/Webs/seo-booster --auth-env-var GRAPHRAG_MCP_TOKEN
144
+ npx sp-rag@latest token verify --token $env:GRAPHRAG_MCP_TOKEN
145
+ ```
146
+
147
+ ## MCP client được hỗ trợ
148
+
149
+ - `codex`
150
+ - `cursor`
151
+ - `claude-code`
152
+ - `antigravity`
153
+ - `vscode`
154
+ - `opencode`
155
+
156
+ ## Skill client được hỗ trợ
157
+
158
+ - `codex` -> `SKILL.md`
159
+ - `claude-code` -> `SKILL.md`
160
+ - `antigravity` -> `SKILL.md`
161
+ - `opencode` -> `SKILL.md`
162
+ - `cursor` -> `.cursor/rules/sp-rag.mdc`
163
+ - `vscode` -> `.github/agents/sp-rag.agent.md` + `.github/copilot-instructions.md` hoặc `~/.copilot/agents/sp-rag.agent.md` + `~/.copilot/instructions/sp-rag.instructions.md`
164
+
165
+ Ghi chú:
166
+
167
+ - generated skill luôn được render bằng tiếng Anh
168
+ - `cursor` hiện nên dùng `scope project` cho rule `.mdc`
169
+ - `vscode` hỗ trợ cả `scope project` lẫn `scope global`
170
+ - nếu không muốn lưu token literal vào file config client, dùng `sp-rag mcp add --auth-env-var SP_RAG_MCP_TOKEN`
171
+
172
+ ## Path mặc định quan trọng
173
+
174
+ ### MCP
175
+
176
+ - `codex`: `~/.codex/config.toml`
177
+ - `cursor` project: `.cursor/mcp.json`
178
+ - `cursor` global: `~/.cursor/mcp.json`
179
+ - `claude-code`: `.mcp.json`
180
+ - `antigravity`: `~/.gemini/antigravity/mcp_config.json`
181
+ - `vscode` project: `.vscode/mcp.json`
182
+ - `vscode` global: file `mcp.json` trong user profile của VS Code
183
+ - `opencode` project: `opencode.json`
184
+ - `opencode` global: `~/.config/opencode/opencode.json`
185
+
186
+ ### Skill / rule / custom agent
187
+
188
+ - `codex`: `~/.codex/skills/sp-rag/SKILL.md`
189
+ - `claude-code`: `~/.claude/skills/sp-rag/SKILL.md`
190
+ - `antigravity`: `~/.gemini/antigravity/skills/sp-rag/SKILL.md`
191
+ - `opencode`: `~/.config/opencode/skills/sp-rag/SKILL.md`
192
+ - `cursor` project: `.cursor/rules/sp-rag.mdc`
193
+ - `vscode` project: `.github/agents/sp-rag.agent.md` và `.github/copilot-instructions.md`
194
+ - `vscode` global: `~/.copilot/agents/sp-rag.agent.md` và `~/.copilot/instructions/sp-rag.instructions.md`
195
+
196
+ ## Luồng khuyên dùng cho dev mới
197
+
198
+ ```bash
199
+ sp-rag install --client codex --mcp-token <grc_pat_...> --doctor
200
+ sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
201
+ sp-rag explain --client codex
202
+ sp-rag token add --token <grc_pat_...>
203
+ sp-rag token verify --token <grc_pat_...>
204
+ sp-rag config show
205
+ sp-rag codegraph status
206
+ sp-rag codegraph watch --interval-ms 2000
207
+ sp-rag codegraph runs --limit 5
208
+ sp-rag codegraph metrics
209
+ sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
210
+ sp-rag mcp add antigravity
211
+ sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
212
+ sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
213
+ sp-rag skill install --client codex
214
+ sp-rag skill install --client cursor --scope project --cwd D:/Webs/seo-booster
215
+ sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
216
+ sp-rag eval run --file ./examples/eval-suite.sample.json
217
+ ```
218
+
219
+ ## Lệnh chính
220
+
221
+ ```bash
222
+ sp-rag install --client codex --mcp-token <grc_pat_...> --doctor
223
+ sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
224
+ sp-rag explain --client codex
225
+ sp-rag token add --token <grc_pat_...>
226
+ sp-rag token verify --token <grc_pat_...>
227
+ sp-rag config show
228
+ sp-rag doctor
229
+ sp-rag codegraph status
230
+ sp-rag codegraph watch --interval-ms 2000
231
+ sp-rag codegraph runs --limit 10
232
+ sp-rag codegraph metrics
233
+ sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
234
+ sp-rag codegraph sync --branch master --commit-sha <sha> --webhook-token <token webhook codegraph> --gitlab-job-token <ci-job-token>
235
+ sp-rag docs get public --format md
236
+ sp-rag mcp add codex
237
+ sp-rag mcp add antigravity
238
+ sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
239
+ sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
240
+ sp-rag skill install --client codex
241
+ sp-rag skill install --client cursor --scope project --cwd D:/Webs/seo-booster
242
+ sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
243
+ sp-rag eval run --file ./examples/eval-suite.sample.json
244
+ sp-rag update setup --client codex
245
+ ```
246
+
247
+ ## `doctor` kiểm
248
+
249
+ `sp-rag doctor` giờ kiểm 2 lớp:
250
+
251
+ - ping các endpoint `api`, `mcp`, `codegraph`
252
+ - đọc đúng file MCP của client để kiểm:
253
+ - IDE đang nhìn vào file nào
254
+ - server entry nào được match theo alias hoặc theo endpoint
255
+ - `Authorization` đang là token literal, env var hợp lệ, hay đang thiếu
256
+ - khả năng IDE sẽ bật link đăng nhập OAuth
257
+
258
+ dụ:
259
+
260
+ ```bash
261
+ sp-rag doctor --client vscode --scope project --cwd D:/Webs/seo-booster
262
+ ```
263
+
264
+ Nếu output có dòng:
265
+
266
+ ```text
267
+ - Authorization: thiếu hoàn toàn
268
+ - Khả năng IDE bật link đăng nhập: cao
269
+ ```
270
+
271
+ thì vấn đề thường không nằm ở skill, mà ở chỗ IDE chưa nạp được bearer header từ file MCP thực tế.
272
+
273
+ ## Cấu hình mặc định
274
+
275
+ CLI lưu cấu hình tại:
276
+
277
+ - `~/.sp-rag/config.json`
278
+ - có thể override home dir bằng `SP_RAG_HOME_DIR`
279
+
280
+ CLI có thể lưu thêm:
281
+
282
+ - `mcpToken` cho flow cài nhanh
283
+ - `authEnvVar` nếu muốn client đọc token từ biến môi trường thay vì lưu literal
284
+ - `defaultClient`, `defaultScope`, `skillClient`
285
+
286
+ Giá trị mặc định:
287
+
288
+ - base URL: `https://sp-rag.secomapp.com`
289
+ - MCP URL: `https://sp-rag.secomapp.com/mcp`
290
+ - alias MCP: `sp-rag`
291
+
292
+ ## Evaluation mẫu
293
+
294
+ - file mẫu: [`examples/eval-suite.sample.json`](./examples/eval-suite.sample.json)
295
+
296
+ ## Tài liệu thêm
297
+
298
+ - [Hướng dẫn dev sử dụng SP-RAG](../../docs/runbooks/dev-usage-guide.md)
299
+ - [Runbook CLI `sp-rag`](../../docs/runbooks/sp-rag-cli.md)
300
+ - [Runbook phát hành CLI `sp-rag`](../../docs/runbooks/sp-rag-cli-release.md)
301
+ - [Runbook MCP Public](../../docs/runbooks/mcp-public-clients.md)
package/dist/cli.js CHANGED
@@ -2,9 +2,21 @@ import { readFile } from 'node:fs/promises';
2
2
  import { loadCliConfig, saveCliConfig, } from './lib/config-store.js';
3
3
  import { runEvaluationSuite } from './lib/eval.js';
4
4
  import { defaultBaseUrl, defaultMcpServerAlias, defaultMcpUrl, installMcpConfig, resolveMcpConfigPath, } from './lib/mcp-config.js';
5
+ import { diagnoseMcpConfig, formatMcpDoctorResult } from './lib/doctor.js';
5
6
  import { fetchJson, fetchText, runDoctor } from './lib/http.js';
6
7
  import { installSkill, resolveSkillInstallTarget, } from './lib/skill.js';
7
- const cliVersion = '0.6.7';
8
+ let cliVersionPromise;
9
+ async function resolveCliVersion() {
10
+ if (!cliVersionPromise) {
11
+ cliVersionPromise = readFile(new URL('../package.json', import.meta.url), 'utf8')
12
+ .then((raw) => {
13
+ const packageJson = JSON.parse(raw);
14
+ return packageJson.version?.trim() || '0.0.0';
15
+ })
16
+ .catch(() => '0.0.0');
17
+ }
18
+ return cliVersionPromise;
19
+ }
8
20
  function parseArgv(argv) {
9
21
  const positionals = [];
10
22
  const options = {};
@@ -177,28 +189,28 @@ function resolveAuthForMcp(parsed, defaults) {
177
189
  };
178
190
  }
179
191
  function helpText() {
180
- return `sp-rag - CLI cho setup, MCP, codegraph, eval và skill của SP-RAG
181
-
182
- Lệnh chính:
183
- sp-rag install --client codex|cursor|claude-code|antigravity|vscode|opencode [--base-url URL] [--mcp-url URL] [--scope global|project] [--mcp-token TOKEN] [--auth-env-var ENV_VAR] [--target-dir PATH] [--doctor]
184
- sp-rag add --client codex|cursor|claude-code|antigravity|vscode|opencode [--scope global|project] [--cwd PATH] [--target-dir PATH]
185
- sp-rag init [--base-url URL] [--mcp-url URL] [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--auth-env-var ENV_VAR] [--target-dir PATH]
186
- sp-rag token add --token TOKEN
187
- sp-rag token verify [--token TOKEN] [--base-url URL]
188
- sp-rag config show
189
- sp-rag explain [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH]
190
- sp-rag doctor [--base-url URL]
191
- sp-rag codegraph status [--base-url URL]
192
- sp-rag codegraph watch [--base-url URL] [--interval-ms N] [--max-polls N]
193
- sp-rag codegraph runs [--base-url URL] [--limit N]
194
- sp-rag codegraph metrics [--base-url URL]
195
- sp-rag codegraph recover [--base-url URL] [--reason TEXT]
196
- sp-rag codegraph sync [--base-url URL] [--branch BRANCH] [--commit-sha SHA] [--force] [--webhook-token TOKEN] [--gitlab-job-token TOKEN]
197
- sp-rag docs get <public|function|dev> [--base-url URL] [--format md|json|html]
198
- sp-rag mcp add <codex|cursor|claude-code|antigravity|vscode|opencode> [--url URL] [--scope global|project] [--auth-env-var ENV_VAR] [--mcp-token TOKEN]
199
- sp-rag skill install [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH] [--target-dir PATH] [--mcp-url URL] [--docs-url URL]
200
- sp-rag eval run --file eval-suite.json [--base-url URL]
201
- sp-rag update setup [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--url URL] [--auth-env-var ENV_VAR] [--target-dir PATH]
192
+ return `sp-rag - CLI cho setup, MCP, codegraph, eval và skill của SP-RAG
193
+
194
+ Lệnh chính:
195
+ sp-rag install --client codex|cursor|claude-code|antigravity|vscode|opencode [--base-url URL] [--mcp-url URL] [--scope global|project] [--mcp-token TOKEN] [--auth-env-var ENV_VAR] [--target-dir PATH] [--doctor]
196
+ sp-rag add --client codex|cursor|claude-code|antigravity|vscode|opencode [--scope global|project] [--cwd PATH] [--target-dir PATH]
197
+ sp-rag init [--base-url URL] [--mcp-url URL] [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--auth-env-var ENV_VAR] [--target-dir PATH]
198
+ sp-rag token add --token TOKEN
199
+ sp-rag token verify [--token TOKEN] [--base-url URL]
200
+ sp-rag config show
201
+ sp-rag explain [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH]
202
+ sp-rag doctor [--base-url URL] [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH]
203
+ sp-rag codegraph status [--base-url URL]
204
+ sp-rag codegraph watch [--base-url URL] [--interval-ms N] [--max-polls N]
205
+ sp-rag codegraph runs [--base-url URL] [--limit N]
206
+ sp-rag codegraph metrics [--base-url URL]
207
+ sp-rag codegraph recover [--base-url URL] [--reason TEXT]
208
+ sp-rag codegraph sync [--base-url URL] [--branch BRANCH] [--commit-sha SHA] [--force] [--webhook-token TOKEN] [--gitlab-job-token TOKEN]
209
+ sp-rag docs get <public|function|dev> [--base-url URL] [--format md|json|html]
210
+ sp-rag mcp add <codex|cursor|claude-code|antigravity|vscode|opencode> [--url URL] [--scope global|project] [--auth-env-var ENV_VAR] [--mcp-token TOKEN]
211
+ sp-rag skill install [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH] [--target-dir PATH] [--mcp-url URL] [--docs-url URL]
212
+ sp-rag eval run --file eval-suite.json [--base-url URL]
213
+ sp-rag update setup [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--url URL] [--auth-env-var ENV_VAR] [--target-dir PATH]
202
214
  `;
203
215
  }
204
216
  function buildCliConfig(defaults) {
@@ -375,6 +387,28 @@ async function runSkillInstall(parsed, defaults, explicitClient) {
375
387
  async function saveResolvedConfig(defaults) {
376
388
  return saveCliConfig(buildCliConfig(defaults), defaults.homeDir);
377
389
  }
390
+ async function emitDoctorReport(parsed, defaults, explicitClient, checks) {
391
+ const results = checks ?? (await runDoctor({ baseUrl: defaults.baseUrl }));
392
+ for (const result of results) {
393
+ process.stdout.write(`${result.ok ? 'OK' : 'FAIL'} ${result.name} ${result.status} ${result.url}\n`);
394
+ }
395
+ const client = resolveSelectedClient(parsed, defaults, explicitClient);
396
+ if (!client) {
397
+ process.stdout.write('INFO doctor bỏ qua chẩn đoán file MCP vì chưa xác định được client.\n');
398
+ return undefined;
399
+ }
400
+ const mcpDiagnostic = await diagnoseMcpConfig({
401
+ client,
402
+ url: defaults.mcpUrl,
403
+ serverAlias: defaults.serverAlias,
404
+ scope: supportedScope(optionString(parsed, 'scope')) ?? defaults.defaultScope,
405
+ cwd: optionString(parsed, 'cwd'),
406
+ });
407
+ for (const line of formatMcpDoctorResult(mcpDiagnostic)) {
408
+ process.stdout.write(`${line}\n`);
409
+ }
410
+ return mcpDiagnostic;
411
+ }
378
412
  async function runClientSetup(parsed, defaults, client) {
379
413
  const nextDefaults = deriveDefaultsForClient(parsed, defaults, client);
380
414
  const configPath = await saveResolvedConfig(nextDefaults);
@@ -386,10 +420,7 @@ async function runClientSetup(parsed, defaults, client) {
386
420
  await runSkillInstall(parsed, nextDefaults, client);
387
421
  }
388
422
  if (optionFlag(parsed, 'doctor')) {
389
- const results = await runDoctor({ baseUrl: nextDefaults.baseUrl });
390
- for (const result of results) {
391
- process.stdout.write(`${result.ok ? 'OK' : 'FAIL'} ${result.name} ${result.status} ${result.url}\n`);
392
- }
423
+ await emitDoctorReport(parsed, nextDefaults, client);
393
424
  }
394
425
  }
395
426
  async function runInit(parsed) {
@@ -405,10 +436,7 @@ async function runInit(parsed) {
405
436
  await runSkillInstall(parsed, nextDefaults, client);
406
437
  }
407
438
  if (optionFlag(parsed, 'doctor')) {
408
- const results = await runDoctor({ baseUrl: nextDefaults.baseUrl });
409
- for (const result of results) {
410
- process.stdout.write(`${result.ok ? 'OK' : 'FAIL'} ${result.name} ${result.status} ${result.url}\n`);
411
- }
439
+ await emitDoctorReport(parsed, nextDefaults, client);
412
440
  }
413
441
  }
414
442
  async function runInstall(parsed) {
@@ -560,7 +588,7 @@ export async function runCli(argv) {
560
588
  const [group, action] = parsed.positionals;
561
589
  try {
562
590
  if (optionFlag(parsed, 'version')) {
563
- process.stdout.write(`sp-rag ${cliVersion}\n`);
591
+ process.stdout.write(`sp-rag ${await resolveCliVersion()}\n`);
564
592
  return 0;
565
593
  }
566
594
  if (!group || group === 'help' || group === '--help') {
@@ -597,13 +625,16 @@ export async function runCli(argv) {
597
625
  }
598
626
  if (group === 'doctor') {
599
627
  const defaults = await loadRuntimeDefaults(parsed);
600
- const results = await runDoctor({
601
- baseUrl: defaults.baseUrl,
602
- });
603
- for (const result of results) {
604
- process.stdout.write(`${result.ok ? 'OK' : 'FAIL'} ${result.name} ${result.status} ${result.url}\n`);
628
+ const results = await runDoctor({ baseUrl: defaults.baseUrl });
629
+ const mcpDiagnostic = await emitDoctorReport(parsed, defaults, undefined, results);
630
+ if (!mcpDiagnostic) {
631
+ return results.every((result) => result.ok) ? 0 : 1;
605
632
  }
606
- return results.every((result) => result.ok) ? 0 : 1;
633
+ return results.every((result) => result.ok) &&
634
+ mcpDiagnostic.entryFound &&
635
+ !mcpDiagnostic.likelyOAuthLogin
636
+ ? 0
637
+ : 1;
607
638
  }
608
639
  if (group === 'codegraph' && action === 'status') {
609
640
  const defaults = await loadRuntimeDefaults(parsed);
@@ -655,7 +686,7 @@ export async function runCli(argv) {
655
686
  return 0;
656
687
  }
657
688
  if (group === 'version') {
658
- process.stdout.write(`sp-rag ${cliVersion}\n`);
689
+ process.stdout.write(`sp-rag ${await resolveCliVersion()}\n`);
659
690
  return 0;
660
691
  }
661
692
  process.stdout.write(helpText());
@@ -0,0 +1,275 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { resolveMcpConfigPath, } from './mcp-config.js';
3
+ function normalizeEndpoint(value) {
4
+ if (typeof value !== 'string') {
5
+ return undefined;
6
+ }
7
+ const trimmed = value.trim();
8
+ return trimmed ? trimmed.replace(/\/+$/, '') : undefined;
9
+ }
10
+ function parseJsonEntries(raw, client) {
11
+ const parsed = JSON.parse(raw);
12
+ let sectionKey = 'mcpServers';
13
+ let endpointKey = 'url';
14
+ if (client === 'vscode') {
15
+ sectionKey = 'servers';
16
+ }
17
+ else if (client === 'opencode') {
18
+ sectionKey = 'mcp';
19
+ }
20
+ else if (client === 'antigravity') {
21
+ endpointKey = 'serverUrl';
22
+ }
23
+ const section = parsed[sectionKey] && typeof parsed[sectionKey] === 'object'
24
+ ? parsed[sectionKey]
25
+ : {};
26
+ return Object.entries(section).flatMap(([alias, value]) => {
27
+ if (!value || typeof value !== 'object') {
28
+ return [];
29
+ }
30
+ const entry = value;
31
+ const headers = parseStringHeaders(entry.headers);
32
+ return [
33
+ {
34
+ alias,
35
+ endpoint: normalizeEndpoint(entry[endpointKey]),
36
+ headers,
37
+ oauth: entry.oauth,
38
+ },
39
+ ];
40
+ });
41
+ }
42
+ function parseStringHeaders(value) {
43
+ if (!value || typeof value !== 'object') {
44
+ return undefined;
45
+ }
46
+ const headers = {};
47
+ for (const [key, headerValue] of Object.entries(value)) {
48
+ if (typeof headerValue === 'string') {
49
+ headers[key] = headerValue;
50
+ }
51
+ }
52
+ return Object.keys(headers).length > 0 ? headers : undefined;
53
+ }
54
+ function parseTomlEntries(raw) {
55
+ const entries = new Map();
56
+ let currentAlias;
57
+ let currentSection;
58
+ for (const originalLine of raw.split(/\r?\n/)) {
59
+ const line = originalLine.trim();
60
+ if (!line || line.startsWith('#')) {
61
+ continue;
62
+ }
63
+ const headersMatch = line.match(/^\[mcp_servers\.(?:"([^"]+)"|([^\].]+))\.headers\]$/);
64
+ if (headersMatch) {
65
+ currentAlias = headersMatch[1] || headersMatch[2];
66
+ currentSection = 'headers';
67
+ if (!entries.has(currentAlias)) {
68
+ entries.set(currentAlias, { alias: currentAlias });
69
+ }
70
+ continue;
71
+ }
72
+ const serverMatch = line.match(/^\[mcp_servers\.(?:"([^"]+)"|([^\].]+))\]$/);
73
+ if (serverMatch) {
74
+ currentAlias = serverMatch[1] || serverMatch[2];
75
+ currentSection = 'server';
76
+ if (!entries.has(currentAlias)) {
77
+ entries.set(currentAlias, { alias: currentAlias });
78
+ }
79
+ continue;
80
+ }
81
+ if (!currentAlias || !currentSection) {
82
+ continue;
83
+ }
84
+ const keyValueMatch = line.match(/^([A-Za-z0-9_-]+)\s*=\s*"(.+)"$/);
85
+ if (!keyValueMatch) {
86
+ continue;
87
+ }
88
+ const [, key, value] = keyValueMatch;
89
+ const entry = entries.get(currentAlias) ?? { alias: currentAlias };
90
+ if (currentSection === 'server' && key === 'url') {
91
+ entry.endpoint = normalizeEndpoint(value);
92
+ }
93
+ if (currentSection === 'headers') {
94
+ entry.headers = {
95
+ ...(entry.headers ?? {}),
96
+ [key]: value,
97
+ };
98
+ }
99
+ entries.set(currentAlias, entry);
100
+ }
101
+ return [...entries.values()];
102
+ }
103
+ function findEntry(entries, serverAlias, targetUrl) {
104
+ const byAlias = entries.find((entry) => entry.alias === serverAlias);
105
+ if (byAlias) {
106
+ return {
107
+ entry: byAlias,
108
+ matchedBy: 'alias',
109
+ };
110
+ }
111
+ const normalizedTarget = normalizeEndpoint(targetUrl);
112
+ if (!normalizedTarget) {
113
+ return {};
114
+ }
115
+ const byEndpoint = entries.find((entry) => entry.endpoint === normalizedTarget);
116
+ if (byEndpoint) {
117
+ return {
118
+ entry: byEndpoint,
119
+ matchedBy: 'endpoint',
120
+ };
121
+ }
122
+ return {};
123
+ }
124
+ function analyzeAuthorization(headerValue) {
125
+ if (!headerValue?.trim()) {
126
+ return {
127
+ authorizationState: 'missing',
128
+ likelyOAuthLogin: true,
129
+ };
130
+ }
131
+ const trimmed = headerValue.trim();
132
+ const envStylePatterns = [
133
+ /\$\{env:([A-Za-z_][A-Za-z0-9_]*)\}/,
134
+ /\{env:([A-Za-z_][A-Za-z0-9_]*)\}/,
135
+ /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/,
136
+ ];
137
+ for (const pattern of envStylePatterns) {
138
+ const match = trimmed.match(pattern);
139
+ if (!match) {
140
+ continue;
141
+ }
142
+ const envVar = match[1];
143
+ const envValue = process.env[envVar]?.trim();
144
+ return {
145
+ authorizationState: envValue ? 'env-present' : 'env-missing',
146
+ authorizationSource: 'env',
147
+ authorizationEnvVar: envVar,
148
+ likelyOAuthLogin: !envValue,
149
+ };
150
+ }
151
+ if (/^Bearer\s+\S+/i.test(trimmed)) {
152
+ return {
153
+ authorizationState: 'present',
154
+ authorizationSource: 'literal',
155
+ likelyOAuthLogin: false,
156
+ };
157
+ }
158
+ return {
159
+ authorizationState: 'invalid',
160
+ likelyOAuthLogin: true,
161
+ };
162
+ }
163
+ function entriesForClient(raw, client) {
164
+ if (client === 'codex') {
165
+ return parseTomlEntries(raw);
166
+ }
167
+ return parseJsonEntries(raw, client);
168
+ }
169
+ export async function diagnoseMcpConfig(options) {
170
+ const resolved = resolveMcpConfigPath({
171
+ client: options.client,
172
+ url: options.url,
173
+ serverAlias: options.serverAlias,
174
+ scope: options.scope,
175
+ cwd: options.cwd,
176
+ });
177
+ const baseResult = {
178
+ client: options.client,
179
+ path: resolved.path,
180
+ scope: resolved.scope,
181
+ exists: false,
182
+ entryFound: false,
183
+ authorizationState: 'missing',
184
+ likelyOAuthLogin: true,
185
+ issues: [],
186
+ };
187
+ let raw = '';
188
+ try {
189
+ raw = await readFile(resolved.path, 'utf8');
190
+ }
191
+ catch (error) {
192
+ return {
193
+ ...baseResult,
194
+ issues: [
195
+ `Không đọc được file MCP tại ${resolved.path}: ${error instanceof Error ? error.message : String(error)}`,
196
+ ],
197
+ };
198
+ }
199
+ const entries = entriesForClient(raw, options.client);
200
+ const { entry, matchedBy } = findEntry(entries, options.serverAlias, options.url);
201
+ if (!entry) {
202
+ return {
203
+ ...baseResult,
204
+ exists: true,
205
+ issues: [
206
+ `Không tìm thấy server MCP alias ${options.serverAlias} hoặc endpoint ${options.url} trong file config.`,
207
+ ],
208
+ };
209
+ }
210
+ const authorization = analyzeAuthorization(entry.headers?.Authorization ?? entry.headers?.authorization);
211
+ const issues = [...baseResult.issues];
212
+ if (authorization.authorizationState === 'missing') {
213
+ issues.push('Thiếu Authorization header. IDE rất dễ rơi sang luồng đăng nhập OAuth.');
214
+ }
215
+ else if (authorization.authorizationState === 'env-missing') {
216
+ issues.push(`Authorization đang dùng biến môi trường ${authorization.authorizationEnvVar} nhưng biến này chưa có giá trị trong phiên hiện tại.`);
217
+ }
218
+ else if (authorization.authorizationState === 'invalid') {
219
+ issues.push('Authorization header có định dạng lạ, IDE có thể bỏ qua và bật OAuth.');
220
+ }
221
+ const oauthDisabled = options.client === 'opencode' && typeof entry.oauth === 'boolean'
222
+ ? entry.oauth === false
223
+ : undefined;
224
+ if (options.client === 'opencode' && authorization.authorizationState !== 'missing' && oauthDisabled !== true) {
225
+ issues.push('OpenCode chưa có oauth=false dù đã cấu hình Authorization header.');
226
+ }
227
+ return {
228
+ ...baseResult,
229
+ exists: true,
230
+ entryFound: true,
231
+ matchedAlias: entry.alias,
232
+ matchedBy,
233
+ endpoint: entry.endpoint,
234
+ oauthDisabled,
235
+ issues,
236
+ ...authorization,
237
+ };
238
+ }
239
+ export function formatMcpDoctorResult(result) {
240
+ const lines = [
241
+ `MCP config cho ${result.client}: ${result.path} (${result.scope})`,
242
+ ];
243
+ if (!result.exists) {
244
+ lines.push(`- Trạng thái file: thiếu hoặc không đọc được`);
245
+ for (const issue of result.issues) {
246
+ lines.push(`- Cảnh báo: ${issue}`);
247
+ }
248
+ return lines;
249
+ }
250
+ lines.push(`- Trạng thái file: đã đọc được`);
251
+ lines.push(`- Server entry: ${result.entryFound
252
+ ? `${result.matchedAlias} (${result.matchedBy === 'endpoint' ? 'khớp theo endpoint' : 'khớp theo alias'})`
253
+ : 'không tìm thấy'}`);
254
+ if (result.endpoint) {
255
+ lines.push(`- Endpoint thực tế: ${result.endpoint}`);
256
+ }
257
+ const authText = result.authorizationState === 'present'
258
+ ? 'đã có bearer token literal'
259
+ : result.authorizationState === 'env-present'
260
+ ? `dùng biến môi trường ${result.authorizationEnvVar} và đang có giá trị`
261
+ : result.authorizationState === 'env-missing'
262
+ ? `dùng biến môi trường ${result.authorizationEnvVar} nhưng hiện chưa có giá trị`
263
+ : result.authorizationState === 'invalid'
264
+ ? 'có header nhưng định dạng không hợp lệ'
265
+ : 'thiếu hoàn toàn';
266
+ lines.push(`- Authorization: ${authText}`);
267
+ if (typeof result.oauthDisabled === 'boolean') {
268
+ lines.push(`- oauth=false: ${result.oauthDisabled ? 'có' : 'không'}`);
269
+ }
270
+ lines.push(`- Khả năng IDE bật link đăng nhập: ${result.likelyOAuthLogin ? 'cao' : 'thấp'}`);
271
+ for (const issue of result.issues) {
272
+ lines.push(`- Cảnh báo: ${issue}`);
273
+ }
274
+ return lines;
275
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sp-rag",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
4
4
  "description": "CLI cho setup MCP, codegraph GitNexus và skill của SP-RAG",
5
5
  "type": "module",
6
6
  "files": [