mcp-xray-pilot 0.10.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.
Files changed (153) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +502 -0
  3. package/data/docs/_index.json +533 -0
  4. package/data/docs/basic__api.md +148 -0
  5. package/data/docs/basic__dns.md +366 -0
  6. package/data/docs/basic__fakedns.md +202 -0
  7. package/data/docs/basic__geodata.md +64 -0
  8. package/data/docs/basic__inbound.md +159 -0
  9. package/data/docs/basic__index.md +136 -0
  10. package/data/docs/basic__log.md +67 -0
  11. package/data/docs/basic__metrics.md +262 -0
  12. package/data/docs/basic__observatory.md +115 -0
  13. package/data/docs/basic__outbound.md +164 -0
  14. package/data/docs/basic__policy.md +140 -0
  15. package/data/docs/basic__reverse.md +268 -0
  16. package/data/docs/basic__routing.md +474 -0
  17. package/data/docs/basic__stats.md +61 -0
  18. package/data/docs/basic__transport.md +1283 -0
  19. package/data/docs/features__features_browser_dialer.md +61 -0
  20. package/data/docs/features__features_env.md +66 -0
  21. package/data/docs/features__features_fallback.md +110 -0
  22. package/data/docs/features__features_index.md +17 -0
  23. package/data/docs/features__features_multiple.md +144 -0
  24. package/data/docs/features__features_xtls.md +13 -0
  25. package/data/docs/inbounds__inbounds_dokodemo.md +11 -0
  26. package/data/docs/inbounds__inbounds_http.md +80 -0
  27. package/data/docs/inbounds__inbounds_hysteria.md +60 -0
  28. package/data/docs/inbounds__inbounds_index.md +22 -0
  29. package/data/docs/inbounds__inbounds_shadowsocks.md +118 -0
  30. package/data/docs/inbounds__inbounds_socks.md +87 -0
  31. package/data/docs/inbounds__inbounds_trojan.md +78 -0
  32. package/data/docs/inbounds__inbounds_tun.md +47 -0
  33. package/data/docs/inbounds__inbounds_tunnel.md +86 -0
  34. package/data/docs/inbounds__inbounds_vless.md +135 -0
  35. package/data/docs/inbounds__inbounds_vmess.md +95 -0
  36. package/data/docs/inbounds__inbounds_wireguard.md +78 -0
  37. package/data/docs/outbounds__outbounds_blackhole.md +42 -0
  38. package/data/docs/outbounds__outbounds_dns.md +97 -0
  39. package/data/docs/outbounds__outbounds_freedom.md +170 -0
  40. package/data/docs/outbounds__outbounds_http.md +70 -0
  41. package/data/docs/outbounds__outbounds_hysteria.md +39 -0
  42. package/data/docs/outbounds__outbounds_index.md +24 -0
  43. package/data/docs/outbounds__outbounds_loopback.md +65 -0
  44. package/data/docs/outbounds__outbounds_shadowsocks.md +105 -0
  45. package/data/docs/outbounds__outbounds_socks.md +58 -0
  46. package/data/docs/outbounds__outbounds_trojan.md +49 -0
  47. package/data/docs/outbounds__outbounds_vless.md +122 -0
  48. package/data/docs/outbounds__outbounds_vmess.md +76 -0
  49. package/data/docs/outbounds__outbounds_wireguard.md +141 -0
  50. package/data/docs/transports__transports_grpc.md +137 -0
  51. package/data/docs/transports__transports_h2.md +11 -0
  52. package/data/docs/transports__transports_http.md +11 -0
  53. package/data/docs/transports__transports_httpupgrade.md +61 -0
  54. package/data/docs/transports__transports_hysteria.md +110 -0
  55. package/data/docs/transports__transports_index.md +19 -0
  56. package/data/docs/transports__transports_mkcp.md +125 -0
  57. package/data/docs/transports__transports_quic.md +11 -0
  58. package/data/docs/transports__transports_raw.md +156 -0
  59. package/data/docs/transports__transports_splithttp.md +11 -0
  60. package/data/docs/transports__transports_tcp.md +11 -0
  61. package/data/docs/transports__transports_websocket.md +75 -0
  62. package/data/docs/transports__transports_xhttp.md +11 -0
  63. package/dist/data/compatibility.js +170 -0
  64. package/dist/data/compatibility.js.map +1 -0
  65. package/dist/data/geocatalogue.js +191 -0
  66. package/dist/data/geocatalogue.js.map +1 -0
  67. package/dist/docs.js +339 -0
  68. package/dist/docs.js.map +1 -0
  69. package/dist/handlers.js +217 -0
  70. package/dist/handlers.js.map +1 -0
  71. package/dist/index.js +66 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/lint.js +737 -0
  74. package/dist/lint.js.map +1 -0
  75. package/dist/schemas/protocols/blackhole.js +16 -0
  76. package/dist/schemas/protocols/blackhole.js.map +1 -0
  77. package/dist/schemas/protocols/common.js +32 -0
  78. package/dist/schemas/protocols/common.js.map +1 -0
  79. package/dist/schemas/protocols/dns.js +14 -0
  80. package/dist/schemas/protocols/dns.js.map +1 -0
  81. package/dist/schemas/protocols/dokodemo.js +17 -0
  82. package/dist/schemas/protocols/dokodemo.js.map +1 -0
  83. package/dist/schemas/protocols/freedom.js +45 -0
  84. package/dist/schemas/protocols/freedom.js.map +1 -0
  85. package/dist/schemas/protocols/http.js +38 -0
  86. package/dist/schemas/protocols/http.js.map +1 -0
  87. package/dist/schemas/protocols/hysteria.js +51 -0
  88. package/dist/schemas/protocols/hysteria.js.map +1 -0
  89. package/dist/schemas/protocols/index.js +50 -0
  90. package/dist/schemas/protocols/index.js.map +1 -0
  91. package/dist/schemas/protocols/loopback.js +11 -0
  92. package/dist/schemas/protocols/loopback.js.map +1 -0
  93. package/dist/schemas/protocols/shadowsocks.js +60 -0
  94. package/dist/schemas/protocols/shadowsocks.js.map +1 -0
  95. package/dist/schemas/protocols/socks.js +42 -0
  96. package/dist/schemas/protocols/socks.js.map +1 -0
  97. package/dist/schemas/protocols/trojan.js +34 -0
  98. package/dist/schemas/protocols/trojan.js.map +1 -0
  99. package/dist/schemas/protocols/tun.js +19 -0
  100. package/dist/schemas/protocols/tun.js.map +1 -0
  101. package/dist/schemas/protocols/vless.js +44 -0
  102. package/dist/schemas/protocols/vless.js.map +1 -0
  103. package/dist/schemas/protocols/vmess.js +48 -0
  104. package/dist/schemas/protocols/vmess.js.map +1 -0
  105. package/dist/schemas/protocols/wireguard.js +34 -0
  106. package/dist/schemas/protocols/wireguard.js.map +1 -0
  107. package/dist/schemas/security/index.js +16 -0
  108. package/dist/schemas/security/index.js.map +1 -0
  109. package/dist/schemas/security/reality.js +35 -0
  110. package/dist/schemas/security/reality.js.map +1 -0
  111. package/dist/schemas/security/tls.js +46 -0
  112. package/dist/schemas/security/tls.js.map +1 -0
  113. package/dist/schemas/security/xtls.js +17 -0
  114. package/dist/schemas/security/xtls.js.map +1 -0
  115. package/dist/schemas/transports/grpc.js +18 -0
  116. package/dist/schemas/transports/grpc.js.map +1 -0
  117. package/dist/schemas/transports/httpupgrade.js +14 -0
  118. package/dist/schemas/transports/httpupgrade.js.map +1 -0
  119. package/dist/schemas/transports/hysteria.js +25 -0
  120. package/dist/schemas/transports/hysteria.js.map +1 -0
  121. package/dist/schemas/transports/index.js +32 -0
  122. package/dist/schemas/transports/index.js.map +1 -0
  123. package/dist/schemas/transports/mkcp.js +34 -0
  124. package/dist/schemas/transports/mkcp.js.map +1 -0
  125. package/dist/schemas/transports/raw.js +19 -0
  126. package/dist/schemas/transports/raw.js.map +1 -0
  127. package/dist/schemas/transports/websocket.js +15 -0
  128. package/dist/schemas/transports/websocket.js.map +1 -0
  129. package/dist/schemas/transports/xhttp.js +34 -0
  130. package/dist/schemas/transports/xhttp.js.map +1 -0
  131. package/dist/search.js +78 -0
  132. package/dist/search.js.map +1 -0
  133. package/dist/state.js +87 -0
  134. package/dist/state.js.map +1 -0
  135. package/dist/tools.js +274 -0
  136. package/dist/tools.js.map +1 -0
  137. package/dist/tools_impl/diff.js +55 -0
  138. package/dist/tools_impl/diff.js.map +1 -0
  139. package/dist/tools_impl/github.js +416 -0
  140. package/dist/tools_impl/github.js.map +1 -0
  141. package/dist/tools_impl/merge.js +181 -0
  142. package/dist/tools_impl/merge.js.map +1 -0
  143. package/dist/tools_impl/refresh.js +46 -0
  144. package/dist/tools_impl/refresh.js.map +1 -0
  145. package/dist/tools_impl/suggest.js +169 -0
  146. package/dist/tools_impl/suggest.js.map +1 -0
  147. package/dist/types.js +10 -0
  148. package/dist/types.js.map +1 -0
  149. package/dist/utils.js +81 -0
  150. package/dist/utils.js.map +1 -0
  151. package/dist/validate.js +408 -0
  152. package/dist/validate.js.map +1 -0
  153. package/package.json +62 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 beekamai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,502 @@
1
+ # mcp-xray-pilot
2
+
3
+ [Русская версия ниже / Russian version below](#mcp-xray-pilot-ru)
4
+
5
+ A Model Context Protocol (MCP) server that gives an LLM offline access to the
6
+ official **xray-core** documentation, plus deep structural validation,
7
+ best-practice lint, a protocol/transport/security compatibility matrix, a
8
+ geosite/geoip catalogue, an alternative-stack suggester and a multi-config
9
+ merge helper.
10
+
11
+ ## What it does
12
+
13
+ - **Bundles ~60 docs pages** from the upstream
14
+ [`XTLS/Xray-docs-next`](https://github.com/XTLS/Xray-docs-next) repo as
15
+ raw markdown (no html→md mess).
16
+ - **Refreshes on demand**: `xray_fetch_topic` tries the network first and
17
+ silently overwrites the bundled cache on success. If you're offline (or
18
+ upstream is down), it falls back to the packaged copy and surfaces a
19
+ `warning` field.
20
+ - **Searches the corpus** with a tiny title/body relevance scorer.
21
+ - **Validates** xray JSON config: required top-level fields, per-protocol
22
+ Zod schemas (vless / vmess / trojan / shadowsocks / socks / http /
23
+ wireguard / hysteria / freedom / blackhole / dns / loopback / dokodemo /
24
+ tun), per-transport `*Settings` schemas (raw / xhttp / grpc / ws / mkcp /
25
+ httpupgrade / hysteria), TLS / REALITY security blocks, routing tag
26
+ cross-references.
27
+ - **Lints** ~20 best-practice rules: VLESS `decryption: "none"`, REALITY
28
+ pubkey/shortId/target syntax, XTLS vision flow compatibility, TLS
29
+ fingerprint enum, ALPN collisions, geosite/geoip typo catcher, protocol
30
+ × transport × security incompatibilities, `xhttp.path` leading slash,
31
+ `geoip:private` block rule, sniffing on 80/443 etc.
32
+ - **Geo catalogue**: search ~500 known geoip/geosite tags by substring.
33
+ - **Compares protocols**: side-by-side table of vless/vmess/trojan/ss/
34
+ hysteria2/wireguard on transports, security, anti-DPI, mobile, battery.
35
+ - **Recommends a stack** for a stated goal (anti-DPI in RU/IR/CN, low
36
+ latency, mobile battery, high throughput, stealth-CDN, getting started).
37
+ - **Merges configs**: joins inbounds/outbounds/routing.rules from N JSON
38
+ configs, auto-resolves tag collisions, warns on port collisions.
39
+
40
+ ## Tools
41
+
42
+ | Tool | What it does |
43
+ | -------------------------- | ------------------------------------------------------------------------------------- |
44
+ | `xray_list_topics` | List doc topics, grouped by category. Use first to discover slugs. |
45
+ | `xray_fetch_topic` | Fetch one topic as markdown. Network → fall back to bundled cache → update cache. |
46
+ | `xray_search` | Full-text search over all cached docs. Returns ranked hits + snippets. |
47
+ | `xray_validate_config` | Structural+schema validation of an xray JSON config (Zod under the hood). |
48
+ | `xray_lint` | ~20 best-practice lint rules. Returns issues with severity, rule id, JSON-pointer. |
49
+ | `xray_geo_search` | Substring search over the embedded geosite/geoip catalogue. |
50
+ | `xray_diff_protocols` | Side-by-side feature table for two protocols. |
51
+ | `xray_suggest_alternative` | Recommend protocol+transport+security for a goal (anti-DPI / battery / latency / …). |
52
+ | `xray_merge_configs` | Merge N xray configs with tag-collision resolution and conflict warnings. |
53
+ | `xray_github_search` | Search issues/PRs/discussions across XTLS GitHub repos (Xray-core/REALITY/docs). |
54
+ | `xray_github_fetch_issue` | Fetch one issue/PR/discussion with full body + top comments. |
55
+ | `xray_refresh_cache` | Bulk re-fetch cached docs (`scope: all/stale/category`). Optional `discover` for new upstream slugs. |
56
+
57
+ It also exposes a single MCP **resource**: `xray://docs/index` — the raw
58
+ `_index.json` of cached topics.
59
+
60
+ ## Examples
61
+
62
+ ### Example 1 — lint a broken cascade config
63
+
64
+ Prompt:
65
+
66
+ > Lint this xray config and tell me what's wrong.
67
+ >
68
+ > ```json
69
+ > {
70
+ > "inbounds":[{"tag":"in1","port":443,"protocol":"vless","settings":{"clients":[{"id":"00000000-0000-4000-8000-000000000000","flow":"xtls-rprx-vision"}]},"streamSettings":{"network":"ws","security":"reality","realitySettings":{"target":"yandex.com","privateKey":"short","shortIds":["zz"],"serverNames":["yandex.com"],"fingerprint":"netscape"}}}],
71
+ > "outbounds":[{"tag":"out","protocol":"freedom"}],
72
+ > "routing":{"rules":[{"type":"field","outboundTag":"missing","domain":["geosite:tinkoff-bank"]}]}
73
+ > }
74
+ > ```
75
+
76
+ `xray_lint` response (excerpt):
77
+
78
+ ```json
79
+ {
80
+ "summary": { "error_count": 6, "warn_count": 5, "info_count": 2 },
81
+ "issues": [
82
+ { "rule": "reality_pubkey_format", "severity": "error",
83
+ "path": "/inbounds/0/streamSettings/realitySettings/privateKey",
84
+ "message": "REALITY privateKey must be 43 base64url chars, got 'short'" },
85
+ { "rule": "reality_shortid_format", "severity": "error",
86
+ "path": "/inbounds/0/streamSettings/realitySettings/shortIds/0",
87
+ "message": "shortIds[0] 'zz' must be hex (0..16 chars, even length)" },
88
+ { "rule": "flow_requires_specific_transport", "severity": "error",
89
+ "path": "/inbounds/0/streamSettings/network",
90
+ "message": "flow=xtls-rprx-vision requires raw/tcp transport, got 'ws'" },
91
+ { "rule": "routing_dangling_outbound", "severity": "error",
92
+ "path": "/routing/rules/0/outboundTag",
93
+ "message": "outboundTag 'missing' does not match any outbound tag" },
94
+ { "rule": "tls_fingerprint_enum", "severity": "warn",
95
+ "path": "/inbounds/0/streamSettings/realitySettings/fingerprint",
96
+ "message": "'netscape' is not a known fingerprint (chrome/firefox/safari/ios/android/edge/360/qq/random/randomized)" },
97
+ { "rule": "geo_unknown_category", "severity": "warn",
98
+ "path": "/routing/rules/0/domain/0",
99
+ "message": "geosite:tinkoff-bank is not in the bundled catalogue (typo? try geosite:category-ru)" }
100
+ ]
101
+ }
102
+ ```
103
+
104
+ ### Example 2 — research RKN bypass via GitHub
105
+
106
+ Prompt:
107
+
108
+ > Find recent xray issues about RKN/TSPU and suggest an anti-DPI stack for Russia.
109
+
110
+ Step 1 — `xray_github_search`:
111
+
112
+ ```jsonc
113
+ // args
114
+ { "query": "RKN", "type": "issue", "repo": "all", "sort": "updated" }
115
+ // response (excerpt)
116
+ {
117
+ "total_count": 47,
118
+ "items": [
119
+ { "repo": "XTLS/Xray-core", "number": 5747, "state": "open",
120
+ "title": "REALITY blocked by TSPU after recent RKN update",
121
+ "comments": 38, "reactions": { "+1": 21 },
122
+ "snippet": "Starting last week our REALITY inbound on :443 stops responding after ~30s of traffic from RU clients. xhttp+REALITY survives longer than raw+vision…" },
123
+ { "repo": "XTLS/Xray-core", "number": 5332, "state": "closed",
124
+ "title": "RKN: shortId enumeration probe",
125
+ "comments": 22,
126
+ "snippet": "TSPU appears to brute-force shortIds. Recommendation: rotate, keep list >=4 entries, do not include empty string…" }
127
+ ]
128
+ }
129
+ ```
130
+
131
+ Step 2 — `xray_suggest_alternative`:
132
+
133
+ ```jsonc
134
+ // args
135
+ { "goal": "anti-dpi-russia" }
136
+ // response (excerpt)
137
+ {
138
+ "recommendation": {
139
+ "protocol": "vless",
140
+ "transport": "xhttp",
141
+ "security": "reality",
142
+ "flow": null,
143
+ "rationale": [
144
+ "xhttp survives TSPU active probing better than raw/tcp+vision in 2025",
145
+ "REALITY hides SNI; pick a target popular in RU (yandex.com, mail.ru)",
146
+ "Rotate shortIds[] (>=4 entries, hex, no empty string)",
147
+ "Keep packet padding default; do not enable kcp on top of REALITY"
148
+ ]
149
+ },
150
+ "see_also": ["xray_fetch_topic transports/xhttp", "xray_fetch_topic features/reality"]
151
+ }
152
+ ```
153
+
154
+ ## Install
155
+
156
+ ```bash
157
+ npm i -g mcp-xray-pilot
158
+ ```
159
+
160
+ Or run from source:
161
+
162
+ ```bash
163
+ git clone https://github.com/beekamai/mcp-xray-pilot.git
164
+ cd mcp-xray-pilot
165
+ npm install
166
+ npm run build
167
+ npm run fetch-docs # fills data/docs/ if you cloned without it
168
+ ```
169
+
170
+ ## Add to Claude Code
171
+
172
+ ```bash
173
+ claude mcp add xray-pilot --scope user -- npx -y mcp-xray-pilot
174
+ ```
175
+
176
+ With a GitHub PAT (raises `xray_github_*` rate limit, enables discussions):
177
+
178
+ ```bash
179
+ claude mcp add xray-pilot --scope user --env GITHUB_TOKEN=ghp_xxx -- npx -y mcp-xray-pilot
180
+ ```
181
+
182
+ Or, from a local clone:
183
+
184
+ ```bash
185
+ claude mcp add xray-pilot --scope user -- node /absolute/path/to/mcp-xray-pilot/dist/index.js
186
+ ```
187
+
188
+ ## Add to Cursor / Windsurf / Cline
189
+
190
+ ```jsonc
191
+ {
192
+ "mcpServers": {
193
+ "xray-pilot": {
194
+ "command": "npx",
195
+ "args": ["-y", "mcp-xray-pilot"]
196
+ }
197
+ }
198
+ }
199
+ ```
200
+
201
+ ## Offline cache vs. online refresh
202
+
203
+ The `data/docs/` directory ships with the package. Each call to
204
+ `xray_fetch_topic`:
205
+
206
+ 1. If `force_offline=true` → reads only the packaged copy.
207
+ 2. Otherwise → tries the upstream raw markdown URL (10s timeout). On HTTP
208
+ 200, the response body replaces the on-disk markdown and the index
209
+ entry's `fetched_at` is updated. Subsequent calls in the same process
210
+ serve from in-memory cache.
211
+ 3. On any network failure → falls back to the packaged copy, returning the
212
+ markdown plus `warning: "network fetch failed: …"`.
213
+
214
+ To refresh everything in bulk, run `npm run fetch-docs -- --refresh`. To
215
+ discover newly-added pages upstream without writing them: `npm run
216
+ fetch-docs -- --discover`.
217
+
218
+ ### Keeping cache fresh
219
+
220
+ There are three complementary ways to keep `data/docs/` aligned with
221
+ upstream:
222
+
223
+ 1. **On-demand per page** — every `xray_fetch_topic` call already tries
224
+ the network first and silently overwrites the on-disk copy on success.
225
+ No action needed.
226
+ 2. **Bulk via MCP tool** — call `xray_refresh_cache` from the LLM:
227
+ - `{ "scope": "stale", "max_age_days": 30 }` (default) re-fetches only
228
+ entries older than N days.
229
+ - `{ "scope": "all" }` re-fetches every page (~60).
230
+ - `{ "scope": "category", "category": "transports" }` restricts to one
231
+ category.
232
+ - Add `"discover": true` to also report slugs that exist upstream but
233
+ are missing from `DOCS_CATALOGUE` in `src/docs.ts`.
234
+ 3. **CI weekly cron** — `.github/workflows/refresh-docs.yml` runs
235
+ `npm run fetch-docs -- --refresh` every Monday 06:00 UTC and opens a
236
+ PR if anything changed (also triggerable manually via `workflow_dispatch`).
237
+
238
+ ## Optional `GITHUB_TOKEN` env variable
239
+
240
+ `xray_github_search` and `xray_github_fetch_issue` work anonymously, but
241
+ the GitHub API caps unauthenticated requests at **60/hour**. Setting
242
+ `GITHUB_TOKEN` to any classic or fine-grained PAT (no scopes needed for
243
+ public repos) raises the limit to **5000/hour** and additionally enables
244
+ the **discussions** endpoint (GraphQL), which has no anonymous access.
245
+
246
+ ```bash
247
+ export GITHUB_TOKEN=ghp_xxx # Linux / macOS
248
+ $env:GITHUB_TOKEN = "ghp_xxx" # PowerShell
249
+ ```
250
+
251
+ When `X-RateLimit-Remaining` drops below 10, the tool surfaces an inline
252
+ warning in the response.
253
+
254
+ ## Roadmap
255
+
256
+ See [ROADMAP.md](./ROADMAP.md). All v0.1–v0.10 milestones are checked off.
257
+
258
+ ## License
259
+
260
+ MIT.
261
+
262
+ ---
263
+
264
+ <a id="mcp-xray-pilot-ru"></a>
265
+
266
+ # mcp-xray-pilot (RU)
267
+
268
+ MCP-сервер, дающий LLM офлайн-доступ к официальной документации **xray-core**,
269
+ плюс глубокую валидацию по схемам, lint best-practice, матрицу
270
+ совместимости протоколов/транспортов/security, каталог geosite/geoip,
271
+ рекомендатор альтернативного стека и helper для слияния конфигов.
272
+
273
+ ## Что делает
274
+
275
+ - **Упаковывает ~60 страниц** документации из upstream-репозитория
276
+ [`XTLS/Xray-docs-next`](https://github.com/XTLS/Xray-docs-next) как
277
+ raw markdown (без html→md мусора).
278
+ - **Обновляется по запросу**: `xray_fetch_topic` сначала идёт в сеть и при
279
+ успехе молча перезаписывает упакованный кеш. Если оффлайн (или upstream
280
+ лёг), возвращает упакованную копию и выставляет `warning`.
281
+ - **Поиск по корпусу** простым title/body relevance scorer.
282
+ - **Валидирует** xray JSON: обязательные top-level поля, per-protocol
283
+ Zod-схемы (vless / vmess / trojan / shadowsocks / socks / http /
284
+ wireguard / hysteria / freedom / blackhole / dns / loopback / dokodemo /
285
+ tun), per-transport `*Settings` (raw / xhttp / grpc / ws / mkcp /
286
+ httpupgrade / hysteria), security блоки TLS/REALITY, routing tag
287
+ cross-references.
288
+ - **Lint** ~20 правил: VLESS `decryption: "none"`, REALITY pubkey/shortId/
289
+ target syntax, XTLS vision flow compatibility, TLS fingerprint enum,
290
+ ALPN collisions, geo typo catcher, protocol × transport × security
291
+ несовместимости, `xhttp.path` слеш, `geoip:private` block, sniffing на
292
+ 80/443 и т.д.
293
+ - **Geo catalogue**: поиск по ~500 известным geoip/geosite тегам.
294
+ - **Сравнение протоколов**: таблица vless/vmess/trojan/ss/hysteria2/
295
+ wireguard по transports, security, anti-DPI, mobile, battery.
296
+ - **Рекомендует стек** под цель (anti-DPI в РФ/Иране/КНР, low-latency,
297
+ mobile-battery, high-throughput, stealth-CDN, getting started).
298
+ - **Сливает конфиги**: объединяет inbounds/outbounds/routing.rules из N
299
+ JSON конфигов, авто-резолвит коллизии тегов, варнит на коллизиях портов.
300
+
301
+ ## Тулы
302
+
303
+ | Тул | Что делает |
304
+ | -------------------------- | --------------------------------------------------------------------------------------- |
305
+ | `xray_list_topics` | Список тем по категориям. Дёргать первым. |
306
+ | `xray_fetch_topic` | Получить тему как markdown. Сеть → fallback на кеш → обновление кеша. |
307
+ | `xray_search` | Полнотекстовый поиск по докам. Хиты + сниппеты. |
308
+ | `xray_validate_config` | Структурная валидация + Zod-схемы по протоколу/transport/security. |
309
+ | `xray_lint` | ~20 правил best-practice. Issues с severity, rule id, JSON-pointer. |
310
+ | `xray_geo_search` | Поиск по embedded каталогу geosite/geoip по подстроке. |
311
+ | `xray_diff_protocols` | Side-by-side таблица фич двух протоколов. |
312
+ | `xray_suggest_alternative` | Рекомендация protocol+transport+security под цель. |
313
+ | `xray_merge_configs` | Слить N конфигов с разрешением коллизий тегов. |
314
+ | `xray_github_search` | Поиск issues/PR/discussions по XTLS GitHub репозиториям. |
315
+ | `xray_github_fetch_issue` | Получить одну issue/PR/discussion с полным body + топ комментариев. |
316
+ | `xray_refresh_cache` | Bulk перезатяжка кеша доков (`scope: all/stale/category`). Опц. `discover` для новых slug'ов. |
317
+
318
+ Также один MCP **ресурс**: `xray://docs/index`.
319
+
320
+ ## Примеры
321
+
322
+ ### Пример 1 — линт сломанного каскадного конфига
323
+
324
+ Промпт:
325
+
326
+ > Прогони линт по этому xray конфигу и скажи что не так.
327
+ >
328
+ > ```json
329
+ > {
330
+ > "inbounds":[{"tag":"in1","port":443,"protocol":"vless","settings":{"clients":[{"id":"00000000-0000-4000-8000-000000000000","flow":"xtls-rprx-vision"}]},"streamSettings":{"network":"ws","security":"reality","realitySettings":{"target":"yandex.com","privateKey":"short","shortIds":["zz"],"serverNames":["yandex.com"],"fingerprint":"netscape"}}}],
331
+ > "outbounds":[{"tag":"out","protocol":"freedom"}],
332
+ > "routing":{"rules":[{"type":"field","outboundTag":"missing","domain":["geosite:tinkoff-bank"]}]}
333
+ > }
334
+ > ```
335
+
336
+ Ответ `xray_lint` (выжимка):
337
+
338
+ ```json
339
+ {
340
+ "summary": { "error_count": 6, "warn_count": 5, "info_count": 2 },
341
+ "issues": [
342
+ { "rule": "reality_pubkey_format", "severity": "error",
343
+ "path": "/inbounds/0/streamSettings/realitySettings/privateKey",
344
+ "message": "REALITY privateKey must be 43 base64url chars, got 'short'" },
345
+ { "rule": "reality_shortid_format", "severity": "error",
346
+ "path": "/inbounds/0/streamSettings/realitySettings/shortIds/0",
347
+ "message": "shortIds[0] 'zz' must be hex (0..16 chars, even length)" },
348
+ { "rule": "flow_requires_specific_transport", "severity": "error",
349
+ "path": "/inbounds/0/streamSettings/network",
350
+ "message": "flow=xtls-rprx-vision requires raw/tcp transport, got 'ws'" },
351
+ { "rule": "routing_dangling_outbound", "severity": "error",
352
+ "path": "/routing/rules/0/outboundTag",
353
+ "message": "outboundTag 'missing' does not match any outbound tag" },
354
+ { "rule": "tls_fingerprint_enum", "severity": "warn",
355
+ "path": "/inbounds/0/streamSettings/realitySettings/fingerprint",
356
+ "message": "'netscape' is not a known fingerprint (chrome/firefox/safari/ios/android/edge/360/qq/random/randomized)" },
357
+ { "rule": "geo_unknown_category", "severity": "warn",
358
+ "path": "/routing/rules/0/domain/0",
359
+ "message": "geosite:tinkoff-bank is not in the bundled catalogue (typo? try geosite:category-ru)" }
360
+ ]
361
+ }
362
+ ```
363
+
364
+ ### Пример 2 — ресёрч обхода РКН через GitHub
365
+
366
+ Промпт:
367
+
368
+ > Найди свежие xray issues про РКН/ТСПУ и предложи anti-DPI стек под Россию.
369
+
370
+ Шаг 1 — `xray_github_search`:
371
+
372
+ ```jsonc
373
+ // args
374
+ { "query": "RKN", "type": "issue", "repo": "all", "sort": "updated" }
375
+ // response (выжимка)
376
+ {
377
+ "total_count": 47,
378
+ "items": [
379
+ { "repo": "XTLS/Xray-core", "number": 5747, "state": "open",
380
+ "title": "REALITY blocked by TSPU after recent RKN update",
381
+ "comments": 38, "reactions": { "+1": 21 },
382
+ "snippet": "Starting last week our REALITY inbound on :443 stops responding after ~30s of traffic from RU clients. xhttp+REALITY survives longer than raw+vision…" },
383
+ { "repo": "XTLS/Xray-core", "number": 5332, "state": "closed",
384
+ "title": "RKN: shortId enumeration probe",
385
+ "comments": 22,
386
+ "snippet": "TSPU appears to brute-force shortIds. Recommendation: rotate, keep list >=4 entries, do not include empty string…" }
387
+ ]
388
+ }
389
+ ```
390
+
391
+ Шаг 2 — `xray_suggest_alternative`:
392
+
393
+ ```jsonc
394
+ // args
395
+ { "goal": "anti-dpi-russia" }
396
+ // response (выжимка)
397
+ {
398
+ "recommendation": {
399
+ "protocol": "vless",
400
+ "transport": "xhttp",
401
+ "security": "reality",
402
+ "flow": null,
403
+ "rationale": [
404
+ "xhttp survives TSPU active probing better than raw/tcp+vision in 2025",
405
+ "REALITY hides SNI; pick a target popular in RU (yandex.com, mail.ru)",
406
+ "Rotate shortIds[] (>=4 entries, hex, no empty string)",
407
+ "Keep packet padding default; do not enable kcp on top of REALITY"
408
+ ]
409
+ },
410
+ "see_also": ["xray_fetch_topic transports/xhttp", "xray_fetch_topic features/reality"]
411
+ }
412
+ ```
413
+
414
+ ## Установка
415
+
416
+ ```bash
417
+ npm i -g mcp-xray-pilot
418
+ ```
419
+
420
+ Или из исходников:
421
+
422
+ ```bash
423
+ git clone https://github.com/beekamai/mcp-xray-pilot.git
424
+ cd mcp-xray-pilot
425
+ npm install
426
+ npm run build
427
+ npm run fetch-docs
428
+ ```
429
+
430
+ ## Подключить к Claude Code
431
+
432
+ ```bash
433
+ claude mcp add xray-pilot --scope user -- npx -y mcp-xray-pilot
434
+ ```
435
+
436
+ С GitHub PAT (поднимает rate-limit `xray_github_*`, включает discussions):
437
+
438
+ ```bash
439
+ claude mcp add xray-pilot --scope user --env GITHUB_TOKEN=ghp_xxx -- npx -y mcp-xray-pilot
440
+ ```
441
+
442
+ Или из локального клона:
443
+
444
+ ```bash
445
+ claude mcp add xray-pilot --scope user -- node /absolute/path/to/mcp-xray-pilot/dist/index.js
446
+ ```
447
+
448
+ ## Офлайн-кеш vs онлайн-обновление
449
+
450
+ Папка `data/docs/` едет в пакете. Каждый вызов `xray_fetch_topic`:
451
+
452
+ 1. При `force_offline=true` → читает только упакованную копию.
453
+ 2. Иначе → пробует upstream raw URL (10s таймаут). При HTTP 200 ответ
454
+ перезаписывает markdown на диске, `fetched_at` обновляется. Последующие
455
+ вызовы в том же процессе отдаются из in-memory кеша.
456
+ 3. При любой сетевой ошибке → fallback на упакованную копию, возвращает
457
+ markdown плюс `warning: "network fetch failed: …"`.
458
+
459
+ Bulk-обновление — `npm run fetch-docs -- --refresh`. Discover новых
460
+ страниц в upstream без записи — `npm run fetch-docs -- --discover`.
461
+
462
+ ### Поддержание кеша актуальным
463
+
464
+ Три способа держать `data/docs/` в синхроне с upstream:
465
+
466
+ 1. **На каждый запрос** — `xray_fetch_topic` сам ходит в сеть и при HTTP
467
+ 200 перезаписывает on-disk копию. Ничего делать не надо.
468
+ 2. **Bulk через MCP-тул** — позови `xray_refresh_cache`:
469
+ - `{ "scope": "stale", "max_age_days": 30 }` (default) — только
470
+ устаревшие старше N дней.
471
+ - `{ "scope": "all" }` — все ~60 страниц.
472
+ - `{ "scope": "category", "category": "transports" }` — одна категория.
473
+ - `"discover": true` — дополнительно вернёт список slug'ов, которые
474
+ появились upstream, но отсутствуют в `DOCS_CATALOGUE` (`src/docs.ts`).
475
+ 3. **CI weekly cron** — `.github/workflows/refresh-docs.yml` каждый
476
+ понедельник 06:00 UTC гоняет `npm run fetch-docs -- --refresh` и
477
+ открывает PR если что-то поменялось (есть `workflow_dispatch` для
478
+ ручного триггера).
479
+
480
+ ## Опциональный `GITHUB_TOKEN`
481
+
482
+ `xray_github_search` и `xray_github_fetch_issue` работают анонимно, но
483
+ GitHub API лимитирует unauth запросы **60/час**. Установка `GITHUB_TOKEN`
484
+ (любой classic или fine-grained PAT, для публичных репо scope не нужен)
485
+ поднимает лимит до **5000/час** и дополнительно включает поиск/чтение
486
+ **discussions** (GraphQL, у него нет anon-доступа).
487
+
488
+ ```bash
489
+ export GITHUB_TOKEN=ghp_xxx # Linux / macOS
490
+ $env:GITHUB_TOKEN = "ghp_xxx" # PowerShell
491
+ ```
492
+
493
+ Когда `X-RateLimit-Remaining` падает ниже 10, тул возвращает inline-warning
494
+ в ответе.
495
+
496
+ ## Roadmap
497
+
498
+ См. [ROADMAP.md](./ROADMAP.md) — все вехи v0.1–v0.10 закрыты.
499
+
500
+ ## Лицензия
501
+
502
+ MIT.