scythe-context-mcp 0.1.0

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.
@@ -0,0 +1,214 @@
1
+ # Gemini Embedding 2 與第三方 v1beta 中轉站相容設計
2
+
3
+ ## 官方 Gemini REST 格式
4
+
5
+ 官方 Gemini Embedding 2 REST endpoint:
6
+
7
+ ```text
8
+ POST https://generativelanguage.googleapis.com/v1beta/models/gemini-embedding-2:embedContent
9
+ ```
10
+
11
+ 官方 key header:
12
+
13
+ ```text
14
+ x-goog-api-key: $GEMINI_API_KEY
15
+ ```
16
+
17
+ Scythe Context 預設設定:
18
+
19
+ ```env
20
+ GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta
21
+ GEMINI_MODEL=gemini-embedding-2
22
+ GEMINI_AUTH_MODE=x-goog-api-key
23
+ GEMINI_API_KEY_HEADER=x-goog-api-key
24
+ GEMINI_OUTPUT_DIMENSIONALITY=1536
25
+ ```
26
+
27
+ ## Embedding 2 query/document 格式
28
+
29
+ Gemini Embedding 2 不用 `task_type` 欄位,而是在文字中加 task prefix。
30
+
31
+ 查詢:
32
+
33
+ ```text
34
+ task: code retrieval | query: {query}
35
+ ```
36
+
37
+ 文件 chunk:
38
+
39
+ ```text
40
+ title: {path or symbol} | text: {chunk}
41
+ ```
42
+
43
+ Scythe Context 的 `GeminiEmbeddingProvider` 會自動套用這個格式。
44
+
45
+ ## Output dimensionality
46
+
47
+ 預設使用 1536 維:
48
+
49
+ ```env
50
+ GEMINI_OUTPUT_DIMENSIONALITY=1536
51
+ ```
52
+
53
+ 建議:
54
+
55
+ - `768`: 省空間,速度快,適合非常小的個人 repo 或低成本模式。
56
+ - `1536`: Scythe Context 預設,精度與成本較平衡。
57
+ - `3072`: 最高精度,但索引體積與相似度計算成本較高。
58
+
59
+ ## 第三方 v1beta 中轉站
60
+
61
+ 很多中轉站會保留 Google v1beta path:
62
+
63
+ ```text
64
+ POST https://proxy.example.com/v1beta/models/gemini-embedding-2:embedContent
65
+ ```
66
+
67
+ 這種情況只要改:
68
+
69
+ ```env
70
+ GEMINI_BASE_URL=https://proxy.example.com/v1beta
71
+ GEMINI_API_KEY=proxy-key
72
+ ```
73
+
74
+ `GEMINI_BASE_URL` 會先正規化,支援下列形式:
75
+
76
+ ```text
77
+ https://proxy.example.com
78
+ https://proxy.example.com/
79
+ https://proxy.example.com/v1beta
80
+ https://proxy.example.com/v1beta/
81
+ https://proxy.example.com/gemini
82
+ https://proxy.example.com/gemini/v1beta/
83
+ ```
84
+
85
+ 如果 URL 最後不是 `v1` 或 `v1beta`,Scythe Context 會自動補上 `/v1beta`。如果中轉站已經給完整 `/v1beta` 或 `/v1`,則不會重複追加。
86
+
87
+ 如果中轉站使用 Bearer token:
88
+
89
+ ```env
90
+ GEMINI_AUTH_MODE=bearer
91
+ ```
92
+
93
+ 會送出:
94
+
95
+ ```text
96
+ Authorization: Bearer proxy-key
97
+ ```
98
+
99
+ 如果中轉站使用 query key:
100
+
101
+ ```env
102
+ GEMINI_AUTH_MODE=query
103
+ GEMINI_API_KEY_QUERY_PARAM=key
104
+ ```
105
+
106
+ 會送出:
107
+
108
+ ```text
109
+ ...?key=proxy-key
110
+ ```
111
+
112
+ 如果中轉站自訂 header:
113
+
114
+ ```env
115
+ GEMINI_AUTH_MODE=x-goog-api-key
116
+ GEMINI_API_KEY_HEADER=Authorization
117
+ ```
118
+
119
+ 注意:這種模式會送 `Authorization: {key}`,不會自動加 `Bearer `;需要 Bearer 時應使用 `GEMINI_AUTH_MODE=bearer`。
120
+
121
+ ## Batch 相容性
122
+
123
+ 官方 batch endpoint:
124
+
125
+ ```text
126
+ POST /v1beta/models/gemini-embedding-2:batchEmbedContents
127
+ ```
128
+
129
+ 部分中轉站可能只支援 `embedContent`,不支援 batch。Phase 2 實作索引時需要:
130
+
131
+ 1. 優先嘗試 batch。
132
+ 2. 如果 batch request 失敗,索引流程 fallback 到逐筆 `embedContent`。
133
+ 3. 後續可加入 provider capability cache,避免每次重試 batch。
134
+
135
+ 目前 `embedBatch` 已按官方 batch 格式送出,`repo_reindex(index_embeddings=true)` 的 embedding writer 會在 batch 失敗時 fallback 到單筆 embedding,並在結果中回報 `batchFallbacks`。
136
+
137
+ ## Codex MCP 設定範例
138
+
139
+ 官方 Gemini:
140
+
141
+ ```toml
142
+ [mcp_servers.scythe_context]
143
+ command = "node"
144
+ args = ["/path/to/scythe-context-mcp/dist/index.js"]
145
+ cwd = "/path/to/scythe-context-mcp"
146
+ env_vars = ["GEMINI_API_KEY"]
147
+
148
+ [mcp_servers.scythe_context.env]
149
+ GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta"
150
+ GEMINI_AUTH_MODE = "x-goog-api-key"
151
+ GEMINI_OUTPUT_DIMENSIONALITY = "1536"
152
+ ```
153
+
154
+ Bearer 中轉站:
155
+
156
+ ```toml
157
+ [mcp_servers.scythe_context]
158
+ command = "node"
159
+ args = ["/path/to/scythe-context-mcp/dist/index.js"]
160
+ cwd = "/path/to/scythe-context-mcp"
161
+ env_vars = ["GEMINI_API_KEY"]
162
+
163
+ [mcp_servers.scythe_context.env]
164
+ GEMINI_BASE_URL = "https://proxy.example.com/v1beta"
165
+ GEMINI_AUTH_MODE = "bearer"
166
+ GEMINI_OUTPUT_DIMENSIONALITY = "1536"
167
+ ```
168
+
169
+ Query key 中轉站:
170
+
171
+ ```toml
172
+ [mcp_servers.scythe_context]
173
+ command = "node"
174
+ args = ["/path/to/scythe-context-mcp/dist/index.js"]
175
+ cwd = "/path/to/scythe-context-mcp"
176
+ env_vars = ["GEMINI_API_KEY"]
177
+
178
+ [mcp_servers.scythe_context.env]
179
+ GEMINI_BASE_URL = "https://proxy.example.com/v1beta"
180
+ GEMINI_AUTH_MODE = "query"
181
+ GEMINI_API_KEY_QUERY_PARAM = "key"
182
+ GEMINI_OUTPUT_DIMENSIONALITY = "1536"
183
+ ```
184
+
185
+ ## 測試方式
186
+
187
+ 啟動 MCP 後呼叫:
188
+
189
+ ```text
190
+ gemini_embedding_probe({ "text": "find code that handles payment logging" })
191
+ ```
192
+
193
+ 成功時應回:
194
+
195
+ ```json
196
+ {
197
+ "status": "ok",
198
+ "latencyMs": 123,
199
+ "model": "gemini-embedding-2",
200
+ "dimensions": 1536,
201
+ "dimensionsMatchExpected": true,
202
+ "sample": [0.01, -0.02]
203
+ }
204
+ ```
205
+
206
+ 失敗時會回傳 `status: "embedding_probe_failed"`、HTTP status、retryable 判斷、截斷後的 response snippet 與可修復建議;不會回傳 API key。
207
+
208
+ 如果失敗,優先檢查:
209
+
210
+ 1. `GEMINI_BASE_URL` 是否是正確中轉站根路徑;可帶或不帶 `/v1beta`。
211
+ 2. 中轉站是否真的支援 `models/{model}:embedContent`。
212
+ 3. auth 是 header、Bearer 還是 query。
213
+ 4. 中轉站是否支援 `output_dimensionality`。
214
+ 5. 模型名稱是否要用 `gemini-embedding-2` 或 `models/gemini-embedding-2`。
@@ -0,0 +1,122 @@
1
+ # 技術棧與效能設計
2
+
3
+ ## Runtime
4
+
5
+ 目標 runtime:
6
+
7
+ - Node.js 24 LTS。
8
+ - `package.json` 設定為 `>=24.11.0 <27`。
9
+ - Node 26 可能可跑,但在進入 LTS 前不作為主要驗收基準。
10
+
11
+ 原因:
12
+
13
+ - Codex MCP server 是本機長駐/短啟動工具,穩定性比追新 API 重要。
14
+ - Node 24 已提供穩定 `fetch`、ESM、Web API 與足夠新的 V8。
15
+ - 避免 Node 20 這類已不適合作為新專案基線的版本。
16
+
17
+ ## Current Dependencies
18
+
19
+ | Package | 用途 | 策略 |
20
+ | --- | --- | --- |
21
+ | `@modelcontextprotocol/sdk` | MCP server / stdio transport | 用最新 1.x 穩定版 |
22
+ | `zod` | MCP tool input schema | 用 v4;MCP SDK 支援 v3/v4 |
23
+ | `dotenv` | 本機 `.env` 載入 | 只用於 dev/local |
24
+ | `fast-glob` | repo scanner | Phase 1 使用 |
25
+ | `ignore` | `.gitignore` 規則 | Phase 1 使用 |
26
+ | `typescript` | typecheck/build | 用目前穩定版 |
27
+ | `tsx` | dev runner | 只用於開發 |
28
+ | `vitest` | unit tests | Phase 1 開始加入 |
29
+ | `better-sqlite3` | SQLite native driver | 同步 API 簡單、效能好,適合本機 MCP |
30
+ | `sqlite-vec` | SQLite vector extension | `vec0(embedding float[1536])` |
31
+
32
+ 暫不引入:
33
+
34
+ - ORM:schema 很小,直接 SQL 更可控。
35
+ - LangChain/LlamaIndex:抽象太厚,對本機 code search 反而增加不可控成本。
36
+ - 外部向量 DB:MVP 不需要啟動 Qdrant/Weaviate 這類服務。
37
+ - tree-sitter:目前先用輕量 regex symbol graph;只有當 retrieval 品質瓶頸明確時才引入。
38
+
39
+ ## Storage Choice
40
+
41
+ 正式索引從 Phase 2 使用:
42
+
43
+ - SQLite metadata。
44
+ - SQLite FTS5 keyword index。
45
+ - sqlite-vec vector table。
46
+
47
+ 1536 維 float32 向量約 6 KB/chunk,不含 SQLite overhead。估算:
48
+
49
+ ```text
50
+ 1,000 chunks ~= 6 MB vector raw data
51
+ 10,000 chunks ~= 60 MB vector raw data
52
+ 50,000 chunks ~= 300 MB vector raw data
53
+ ```
54
+
55
+ 這個量級仍適合本機 SQLite,但不適合 JSON vector 全表解析。
56
+
57
+ Spike 結論:
58
+
59
+ - Node 24 LTS + WSL 可載入 `sqlite-vec`。
60
+ - `better-sqlite3` 可建立 `vec0(embedding float[1536])` virtual table。
61
+ - 寫入 sqlite-vec virtual table 的 `rowid` 需要用 `BigInt` 綁定。
62
+ - vector 以 `Float32Array` buffer 寫入與查詢。
63
+
64
+ ## Performance Targets
65
+
66
+ Phase 1 scanner/chunker:
67
+
68
+ - 小型 repo: < 1,000 files,dry-run 目標 < 2s。
69
+ - 中型 repo: < 10,000 files,dry-run 目標 < 10s。
70
+ - 大檔預設跳過或截斷,不允許單檔拖垮整次索引。
71
+
72
+ Phase 2 indexing:
73
+
74
+ - 增量索引必須以 file hash/chunk hash 避免重複 embedding。
75
+ - embedding batch size 預設 16-64,依 provider 錯誤調整。
76
+ - 中轉站不支援 batch 時 fallback 單筆,但要有 rate limit。
77
+ - `repo_reindex` 預設只寫 metadata;必須顯式 `index_embeddings=true` 才會產生 embedding API 成本。
78
+ - `max_embedding_chunks` 預設限制單次 embedding 數量,避免第一次索引大 repo 時成本失控。
79
+
80
+ Phase 3 search:
81
+
82
+ - 已建索引的小/中型 repo 查詢目標 < 500ms,不含 query embedding 網路時間。
83
+ - query embedding 網路時間單獨計入,tool response 要能指出 provider latency。
84
+ - topK retrieve 先取 50-100,再做 merge/rerank 回傳 8-12 個結果。
85
+
86
+ ## Retrieval Quality Targets
87
+
88
+ 搜尋不能只追 semantic similarity。最低標準:
89
+
90
+ - 自然語言 query 可找到非字面匹配的實作。
91
+ - 精確函式名、檔名、錯誤訊息不能被向量結果蓋掉。
92
+ - 中文 query 對英文程式碼要靠 embedding + keyword extraction 雙路徑補強。
93
+ - 結果必須有路徑、行號、短 snippet、match reason、grep keywords。
94
+
95
+ ## Efficiency Controls
96
+
97
+ 必要控制:
98
+
99
+ - `max_file_bytes`
100
+ - `max_chunk_chars`
101
+ - `max_chunks_per_file`
102
+ - `embedding_batch_size`
103
+ - `embedding_concurrency`
104
+ - `max_embedding_chunks`
105
+ - `max_results`
106
+ - `max_context_chars`
107
+
108
+ 這些要在 Phase 1/2 進入 config,避免工具在大型 repo 上不可控。
109
+
110
+ ## Risk Register
111
+
112
+ | Risk | Impact | Mitigation |
113
+ | --- | --- | --- |
114
+ | 第三方 Gemini 中轉站不支援 batch | 索引變慢 | embedding writer 已有單筆 fallback;後續可加 provider capability cache |
115
+ | sqlite-vec native extension 載入失敗 | Phase 2 卡住 | Spike 已通過;仍保留 FTS-only degrade mode |
116
+ | 大 repo 初次 embedding 成本高 | 慢、花費高 | dry-run 預估 chunks/token/cost;增量索引 |
117
+ | 純語義誤召回 | Codex 讀錯檔 | hybrid ranker、path/symbol boost、grep keywords |
118
+ | 私有碼送到第三方 provider | 隱私風險 | 明確 remote embedding 開關與文檔警告 |
119
+
120
+ ## Recommendation
121
+
122
+ Phase 1 dry-run scanner/chunker 已完成。Phase 2 已完成 `better-sqlite3 + sqlite-vec` 載入 spike、schema 初始化、file/chunk metadata 寫入流程與 embedding index writer。Phase 3 已完成 semantic vector lookup、keyword search 與 hybrid ranker。Phase 5 已完成輕量 symbol graph / related files MVP。Context packer、多跳 related-file traversal、related snippets、provider diagnostics 與 index freshness diagnostics 也已完成。下一步建議是 provider capability cache、錯誤 remediation polish 與必要時的 tree-sitter spike。
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "scythe-context-mcp",
3
+ "version": "0.1.0",
4
+ "description": "Local MCP context engine for Codex with Gemini Embedding 2 support.",
5
+ "type": "module",
6
+ "license": "Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Lianye-Scythe/scythe-context-mcp.git"
10
+ },
11
+ "keywords": [
12
+ "codex",
13
+ "mcp",
14
+ "context-engine",
15
+ "code-search",
16
+ "gemini-embedding",
17
+ "sqlite-vec"
18
+ ],
19
+ "bin": {
20
+ "scythe-context-mcp": "dist/index.js"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "README.md",
25
+ "README.en.md",
26
+ "README.zh-CN.md",
27
+ "CHANGELOG.md",
28
+ "LICENSE",
29
+ "docs",
30
+ ".env.example"
31
+ ],
32
+ "scripts": {
33
+ "build": "tsc -p tsconfig.json",
34
+ "dev": "tsx src/index.ts",
35
+ "prepack": "npm run build",
36
+ "test": "vitest run",
37
+ "typecheck": "tsc -p tsconfig.json --noEmit"
38
+ },
39
+ "engines": {
40
+ "node": ">=24.11.0 <27"
41
+ },
42
+ "dependencies": {
43
+ "@modelcontextprotocol/sdk": "^1.29.0",
44
+ "better-sqlite3": "^12.10.0",
45
+ "dotenv": "^17.4.2",
46
+ "fast-glob": "^3.3.3",
47
+ "ignore": "^7.0.5",
48
+ "sqlite-vec": "^0.1.9",
49
+ "zod": "^4.4.3"
50
+ },
51
+ "devDependencies": {
52
+ "@types/better-sqlite3": "^7.6.13",
53
+ "@types/node": "^24.13.2",
54
+ "tsx": "^4.19.2",
55
+ "typescript": "^6.0.3",
56
+ "vitest": "^4.1.8"
57
+ }
58
+ }