mcp-xray-pilot 0.10.1 → 0.14.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.
package/README.md CHANGED
@@ -10,8 +10,9 @@
10
10
  A Model Context Protocol (MCP) server that gives an LLM offline access to the
11
11
  official **xray-core** documentation, plus deep structural validation,
12
12
  best-practice lint, a protocol/transport/security compatibility matrix, a
13
- geosite/geoip catalogue, an alternative-stack suggester and a multi-config
14
- merge helper.
13
+ **full v2fly geosite catalogue (~1500 categories)**, an alternative-stack
14
+ suggester, REALITY keypair / shortId generators, a live SNI target validator,
15
+ a curated SNI suggester per exit-country and a multi-config merge helper.
15
16
 
16
17
  ## What it does
17
18
 
@@ -29,12 +30,19 @@ merge helper.
29
30
  tun), per-transport `*Settings` schemas (raw / xhttp / grpc / ws / mkcp /
30
31
  httpupgrade / hysteria), TLS / REALITY security blocks, routing tag
31
32
  cross-references.
32
- - **Lints** ~20 best-practice rules: VLESS `decryption: "none"`, REALITY
33
+ - **Lints** ~22 best-practice rules: VLESS `decryption: "none"`, REALITY
33
34
  pubkey/shortId/target syntax, XTLS vision flow compatibility, TLS
34
35
  fingerprint enum, ALPN collisions, geosite/geoip typo catcher, protocol
35
36
  × transport × security incompatibilities, `xhttp.path` leading slash,
36
- `geoip:private` block rule, sniffing on 80/443 etc.
37
- - **Geo catalogue**: search ~500 known geoip/geosite tags by substring.
37
+ `geoip:private` block rule, sniffing on 80/443, **DNS-over-proxy leaks
38
+ on `.ru` direct routing** (v0.12), **`geosite:` categories absent from
39
+ xray-core release `geosite.dat`** (v0.12) etc.
40
+ - **Geo catalogue**: search ~1500 known geoip/geosite tags by substring (full
41
+ v2fly/domain-list-community catalogue, hydrated from `data/geocatalogue.json`).
42
+ - **REALITY toolbelt**: `xray_generate_reality_keypair` (drop-in replacement
43
+ for `xray x25519`), `xray_generate_short_ids` (cryptographically random,
44
+ legacy-empty prefix), `xray_validate_sni_target` (live TLS 1.3 + h2 probe),
45
+ `xray_suggest_sni_for_country` (curated REALITY fronts per exit-country).
38
46
  - **Compares protocols**: side-by-side table of vless/vmess/trojan/ss/
39
47
  hysteria2/wireguard on transports, security, anti-DPI, mobile, battery.
40
48
  - **Recommends a stack** for a stated goal (anti-DPI in RU/IR/CN, low
@@ -50,14 +58,115 @@ merge helper.
50
58
  | `xray_fetch_topic` | Fetch one topic as markdown. Network → fall back to bundled cache → update cache. |
51
59
  | `xray_search` | Full-text search over all cached docs. Returns ranked hits + snippets. |
52
60
  | `xray_validate_config` | Structural+schema validation of an xray JSON config (Zod under the hood). |
53
- | `xray_lint` | ~20 best-practice lint rules. Returns issues with severity, rule id, JSON-pointer. |
54
- | `xray_geo_search` | Substring search over the embedded geosite/geoip catalogue. |
61
+ | `xray_lint` | ~22 best-practice lint rules. Returns issues with severity, rule id, JSON-pointer. |
62
+ | `xray_geo_search` | Substring search over the embedded geosite/geoip catalogue (~1500 tags). |
55
63
  | `xray_diff_protocols` | Side-by-side feature table for two protocols. |
56
64
  | `xray_suggest_alternative` | Recommend protocol+transport+security for a goal (anti-DPI / battery / latency / …). |
65
+ | `xray_generate_short_ids` | Cryptographically random REALITY shortIds (default `[4,8,16]` bytes, legacy empty prefix). |
66
+ | `xray_generate_reality_keypair` | Fresh REALITY X25519 keypair, base64url 43 chars — drop-in for `xray x25519`. |
67
+ | `xray_validate_sni_target` | Live TLS 1.3 + ALPN h2 + HTTP probe of a candidate REALITY target host. |
68
+ | `xray_test_reality_live` | Spin up real local xray server+client, run a full REALITY handshake against the target, probe HTTPS through the cascade. Strictly stronger than `xray_validate_sni_target`. |
69
+ | `xray_whitelist_sni_candidates` | Pull a public RU-traffic whitelist (default: hxehex/russia-mobile-internet-whitelist) and TLS-validate the top N hosts as REALITY SNI front candidates. Returns ranked, sorted by ok desc, latency asc. |
70
+ | `xray_suggest_sni_for_country` | Curated REALITY SNI/target hosts per exit-country (DE/PL/NL/FR/LV/SE/FI/US/UK/JP/SG/AU/CA). |
57
71
  | `xray_merge_configs` | Merge N xray configs with tag-collision resolution and conflict warnings. |
58
72
  | `xray_github_search` | Search issues/PRs/discussions across XTLS GitHub repos (Xray-core/REALITY/docs). |
59
73
  | `xray_github_fetch_issue` | Fetch one issue/PR/discussion with full body + top comments. |
60
- | `xray_refresh_cache` | Bulk re-fetch cached docs (`scope: all/stale/category`). Optional `discover` for new upstream slugs. |
74
+ | `xray_refresh_cache` | Bulk re-fetch cached docs (`scope: all/stale/category`). Optional `discover` for new upstream slugs. Optional `refresh_geocatalogue: true` to also re-pull the v2fly category list. |
75
+
76
+ Total: **18 tools**.
77
+
78
+ ### Quick toolbelt
79
+
80
+ Generate a REALITY keypair (server-side priv + client-side pub):
81
+
82
+ ```jsonc
83
+ // xray_generate_reality_keypair {}
84
+ {
85
+ "privateKey": "MNCibkT-h5bCF6iknJG0rJdHdfUjT7VugHgSX9BRWUY",
86
+ "publicKey": "ffvf0eNwnLhgK-axt3rajJIAoHKv0rX4xkw5KyImn38",
87
+ "note": "Server: paste privateKey into inbound realitySettings.privateKey. Clients: paste publicKey into outbound realitySettings.publicKey. Format matches `xray x25519` output exactly."
88
+ }
89
+ ```
90
+
91
+ Live-check a candidate REALITY SNI:
92
+
93
+ ```jsonc
94
+ // xray_validate_sni_target { "host": "www.onet.pl" }
95
+ {
96
+ "host": "www.onet.pl", "port": 443, "ok": true,
97
+ "tls_version": "TLSv1.3", "alpn": "h2", "http_status": 200,
98
+ "cert_subject": "www.onet.pl", "cert_san_count": 4,
99
+ "latency_ms": 312, "issues": []
100
+ }
101
+ ```
102
+
103
+ Live REALITY handshake (catches what `xray_validate_sni_target` cannot):
104
+
105
+ ```jsonc
106
+ // xray_test_reality_live { "target_host": "2gis.ru" }
107
+ {
108
+ "ok": true,
109
+ "target": "2gis.ru:443",
110
+ "reality_handshake_complete": true,
111
+ "client_received_real_cert": false,
112
+ "http_probe_status": 200,
113
+ "latency_ms": 824,
114
+ "issues": [],
115
+ "used_keypair": { "privateKey": "...", "publicKey": "...", "shortId": "a1b2c3d4" },
116
+ "cached": false,
117
+ "cached_at": "2026-05-06T12:30:00.000Z"
118
+ }
119
+ ```
120
+
121
+ > Verdicts are persisted to `data/reality-verdicts.json` (LRU cap 50, TTL 24h,
122
+ > key `host:port`). Subsequent calls with the same target return instantly with
123
+ > `cached: true`. Pass `force_refresh: true` to bypass the cache and rerun xray.
124
+
125
+ Compare a list of candidates in one shot — runs sequentially, each entry hits
126
+ the cache, results are sorted by `ok desc, latency_ms asc`:
127
+
128
+ ```jsonc
129
+ // xray_test_reality_live {
130
+ // "multi_targets": ["2gis.ru", "yandex.ru", "vk.com", "mail.ru"]
131
+ // }
132
+ {
133
+ "results": [
134
+ { "target": "2gis.ru:443", "ok": true, "latency_ms": 824, "cached": true, "cached_at": "..." },
135
+ { "target": "mail.ru:443", "ok": true, "latency_ms": 1102, "cached": false, "cached_at": "..." },
136
+ { "target": "yandex.ru:443", "ok": false, "latency_ms": 0, "cached": false, "cached_at": "...", "issues": ["client received a real certificate ..."] },
137
+ { "target": "vk.com:443", "ok": false, "latency_ms": 0, "cached": false, "cached_at": "...", "issues": ["target unreachable"] }
138
+ ],
139
+ "summary": { "ok_count": 2, "total": 4 }
140
+ }
141
+ ```
142
+
143
+ Pull a public RU-traffic whitelist and rank live SNI candidates for an
144
+ inbound RU-relay node:
145
+
146
+ ```jsonc
147
+ // xray_whitelist_sni_candidates { "max_candidates": 10 }
148
+ {
149
+ "source_url": "https://raw.githubusercontent.com/hxehex/russia-mobile-internet-whitelist/main/whitelist.txt",
150
+ "fetched_at": "2026-05-06T12:34:00.000Z",
151
+ "total_domains": 412,
152
+ "tested": 10,
153
+ "candidates": [
154
+ { "host": "ya.ru", "ok": true, "tls_version": "TLSv1.3", "alpn": "h2", "http_status": 200, "latency_ms": 41, "cert_subject": "ya.ru", "issues": [] },
155
+ { "host": "vk.com", "ok": true, "tls_version": "TLSv1.3", "alpn": "h2", "http_status": 200, "latency_ms": 78, "cert_subject": "*.vk.com", "issues": [] },
156
+ { "host": "ozon.ru", "ok": false, "tls_version": "TLSv1.3", "alpn": "h2", "http_status": 200, "latency_ms": 134, "cert_subject": "*.ozon.ru", "issues": [] }
157
+ ],
158
+ "summary": { "ok_count": 8, "failed_count": 2 },
159
+ "cache": { "used": false, "age_seconds": null },
160
+ "notes": ["latency_ms is measured from the MCP host (your laptop), not from the relay node — re-test from the node for geo-accurate values"]
161
+ }
162
+ ```
163
+
164
+ > The whitelist body is cached on disk in `data/whitelist-cache.json`
165
+ > (default TTL 24h, override via `cache_ttl_hours`). Verdicts are NOT cached
166
+ > — every call re-probes its slice live so latency numbers stay fresh.
167
+ > NB: the probe runs from your laptop, not from the РФ relay; for
168
+ > geo-accurate latency, use `xray_test_reality_live` from the actual
169
+ > node (or ssh + curl on it).
61
170
 
62
171
  It also exposes a single MCP **resource**: `xray://docs/index` — the raw
63
172
  `_index.json` of cached topics.
@@ -258,7 +367,7 @@ warning in the response.
258
367
 
259
368
  ## Roadmap
260
369
 
261
- See [ROADMAP.md](./ROADMAP.md). All v0.1–v0.10 milestones are checked off.
370
+ See [ROADMAP.md](./ROADMAP.md). All v0.1–v0.11 milestones are checked off.
262
371
 
263
372
  ## License
264
373
 
@@ -290,12 +399,19 @@ MCP-сервер, дающий LLM офлайн-доступ к официаль
290
399
  tun), per-transport `*Settings` (raw / xhttp / grpc / ws / mkcp /
291
400
  httpupgrade / hysteria), security блоки TLS/REALITY, routing tag
292
401
  cross-references.
293
- - **Lint** ~20 правил: VLESS `decryption: "none"`, REALITY pubkey/shortId/
402
+ - **Lint** ~22 правил: VLESS `decryption: "none"`, REALITY pubkey/shortId/
294
403
  target syntax, XTLS vision flow compatibility, TLS fingerprint enum,
295
404
  ALPN collisions, geo typo catcher, protocol × transport × security
296
405
  несовместимости, `xhttp.path` слеш, `geoip:private` block, sniffing на
297
- 80/443 и т.д.
298
- - **Geo catalogue**: поиск по ~500 известным geoip/geosite тегам.
406
+ 80/443, **утечка DNS через прокси ломает `.ru` direct routing** (v0.12),
407
+ **`geosite:` категории, которых нет в xray-core release `geosite.dat`**
408
+ (v0.12) и т.д.
409
+ - **Geo catalogue**: поиск по ~1500 известным geoip/geosite тегам (полный
410
+ v2fly/domain-list-community каталог, гидратируется из `data/geocatalogue.json`).
411
+ - **REALITY toolbelt**: `xray_generate_reality_keypair` (drop-in замена
412
+ `xray x25519`), `xray_generate_short_ids` (криптослучайные, с empty-prefix
413
+ для легаси), `xray_validate_sni_target` (live TLS 1.3 + h2 проба),
414
+ `xray_suggest_sni_for_country` (курируемые REALITY-фронты по стране exit'а).
299
415
  - **Сравнение протоколов**: таблица vless/vmess/trojan/ss/hysteria2/
300
416
  wireguard по transports, security, anti-DPI, mobile, battery.
301
417
  - **Рекомендует стек** под цель (anti-DPI в РФ/Иране/КНР, low-latency,
@@ -312,13 +428,113 @@ MCP-сервер, дающий LLM офлайн-доступ к официаль
312
428
  | `xray_search` | Полнотекстовый поиск по докам. Хиты + сниппеты. |
313
429
  | `xray_validate_config` | Структурная валидация + Zod-схемы по протоколу/transport/security. |
314
430
  | `xray_lint` | ~20 правил best-practice. Issues с severity, rule id, JSON-pointer. |
315
- | `xray_geo_search` | Поиск по embedded каталогу geosite/geoip по подстроке. |
431
+ | `xray_geo_search` | Поиск по embedded каталогу geosite/geoip по подстроке (~1500 тегов). |
316
432
  | `xray_diff_protocols` | Side-by-side таблица фич двух протоколов. |
317
433
  | `xray_suggest_alternative` | Рекомендация protocol+transport+security под цель. |
434
+ | `xray_generate_short_ids` | Криптослучайные REALITY shortIds (default `[4,8,16]` байт, с empty-prefix для легаси). |
435
+ | `xray_generate_reality_keypair` | Свежая X25519 пара REALITY, base64url 43 chars — drop-in для `xray x25519`. |
436
+ | `xray_validate_sni_target` | Live проба TLS 1.3 + ALPN h2 + HTTP кандидата на REALITY target. |
437
+ | `xray_test_reality_live` | Поднимает локальную пару xray (server+client), реально гоняет REALITY handshake к target, probe HTTPS сквозь каскад. Строго сильнее `xray_validate_sni_target`. |
438
+ | `xray_whitelist_sni_candidates` | Тянет публичный whitelist РФ-трафика (default: hxehex/russia-mobile-internet-whitelist), TLS-валидирует топ-N как REALITY SNI-кандидаты. Сортировка ok desc, latency asc. |
439
+ | `xray_suggest_sni_for_country` | Курируемые REALITY-фронты по стране exit'а (DE/PL/NL/FR/LV/SE/FI/US/UK/JP/SG/AU/CA). |
318
440
  | `xray_merge_configs` | Слить N конфигов с разрешением коллизий тегов. |
319
441
  | `xray_github_search` | Поиск issues/PR/discussions по XTLS GitHub репозиториям. |
320
442
  | `xray_github_fetch_issue` | Получить одну issue/PR/discussion с полным body + топ комментариев. |
321
- | `xray_refresh_cache` | Bulk перезатяжка кеша доков (`scope: all/stale/category`). Опц. `discover` для новых slug'ов. |
443
+ | `xray_refresh_cache` | Bulk перезатяжка кеша доков (`scope: all/stale/category`). Опц. `discover` + `refresh_geocatalogue: true` (заодно перетянет v2fly категории). |
444
+
445
+ Всего: **18 тулов**.
446
+
447
+ ### Quick toolbelt
448
+
449
+ Сгенерить REALITY keypair (priv для сервера, pub для клиентов):
450
+
451
+ ```jsonc
452
+ // xray_generate_reality_keypair {}
453
+ {
454
+ "privateKey": "MNCibkT-h5bCF6iknJG0rJdHdfUjT7VugHgSX9BRWUY",
455
+ "publicKey": "ffvf0eNwnLhgK-axt3rajJIAoHKv0rX4xkw5KyImn38",
456
+ "note": "Format matches `xray x25519` output exactly."
457
+ }
458
+ ```
459
+
460
+ Live-проверить кандидата на REALITY SNI:
461
+
462
+ ```jsonc
463
+ // xray_validate_sni_target { "host": "www.onet.pl" }
464
+ {
465
+ "host": "www.onet.pl", "port": 443, "ok": true,
466
+ "tls_version": "TLSv1.3", "alpn": "h2", "http_status": 200,
467
+ "cert_subject": "www.onet.pl", "cert_san_count": 4,
468
+ "latency_ms": 312, "issues": []
469
+ }
470
+ ```
471
+
472
+ > ⚠️ Проба идёт с локальной машины, где запущен `mcp-xray-pilot`. Для решения «работает ли SNI из РФ» — гоняй с РФ-IP отдельно.
473
+
474
+ Реальный REALITY handshake (ловит то, что `xray_validate_sni_target` пропускает):
475
+
476
+ ```jsonc
477
+ // xray_test_reality_live { "target_host": "2gis.ru" }
478
+ {
479
+ "ok": true,
480
+ "target": "2gis.ru:443",
481
+ "reality_handshake_complete": true,
482
+ "client_received_real_cert": false,
483
+ "http_probe_status": 200,
484
+ "latency_ms": 824,
485
+ "issues": [],
486
+ "used_keypair": { "privateKey": "...", "publicKey": "...", "shortId": "a1b2c3d4" },
487
+ "cached": false,
488
+ "cached_at": "2026-05-06T12:30:00.000Z"
489
+ }
490
+ ```
491
+
492
+ > При первом вызове скачивает xray-binary в `~/.cache/mcp-xray-pilot/xray-bin/` (~30MB), дальше переиспользует. Если REALITY несовместим с target (как `outlook.live.com` или `www.ozon.ru` — реальные кейсы Flare VPN), `client_received_real_cert: true` и `ok: false`.
493
+
494
+ > Вердикты пишутся на диск в `data/reality-verdicts.json` (LRU 50, TTL 24h, ключ `host:port`). Повторный вызов на тот же таргет возвращается мгновенно с `cached: true`. Чтобы прогнать заново — `force_refresh: true`.
495
+
496
+ Сравнить пачку кандидатов одним вызовом — запускаются последовательно, каждый через кэш, сорт `ok desc, latency_ms asc`:
497
+
498
+ ```jsonc
499
+ // xray_test_reality_live {
500
+ // "multi_targets": ["2gis.ru", "yandex.ru", "vk.com", "mail.ru"]
501
+ // }
502
+ {
503
+ "results": [
504
+ { "target": "2gis.ru:443", "ok": true, "latency_ms": 824, "cached": true },
505
+ { "target": "mail.ru:443", "ok": true, "latency_ms": 1102, "cached": false },
506
+ { "target": "yandex.ru:443", "ok": false, "issues": ["client received a real certificate ..."] },
507
+ { "target": "vk.com:443", "ok": false, "issues": ["target unreachable"] }
508
+ ],
509
+ "summary": { "ok_count": 2, "total": 4 }
510
+ }
511
+ ```
512
+
513
+ Затянуть публичный whitelist РФ-трафика и проранжировать кандидатов на REALITY SNI для inbound РФ-relay-ноды:
514
+
515
+ ```jsonc
516
+ // xray_whitelist_sni_candidates { "max_candidates": 10 }
517
+ {
518
+ "source_url": "https://raw.githubusercontent.com/hxehex/russia-mobile-internet-whitelist/main/whitelist.txt",
519
+ "fetched_at": "2026-05-06T12:34:00.000Z",
520
+ "total_domains": 412,
521
+ "tested": 10,
522
+ "candidates": [
523
+ { "host": "ya.ru", "ok": true, "tls_version": "TLSv1.3", "alpn": "h2", "http_status": 200, "latency_ms": 41, "cert_subject": "ya.ru", "issues": [] },
524
+ { "host": "vk.com", "ok": true, "tls_version": "TLSv1.3", "alpn": "h2", "http_status": 200, "latency_ms": 78, "cert_subject": "*.vk.com", "issues": [] }
525
+ ],
526
+ "summary": { "ok_count": 8, "failed_count": 2 },
527
+ "cache": { "used": false, "age_seconds": null },
528
+ "notes": ["latency_ms is measured from the MCP host (your laptop), not from the relay node — re-test from the node for geo-accurate values"]
529
+ }
530
+ ```
531
+
532
+ > Тело whitelist кешируется на диск в `data/whitelist-cache.json` (TTL по
533
+ > умолчанию 24h, переопределение через `cache_ttl_hours`). Verdicts НЕ
534
+ > кешируются — каждый вызов делает свежие пробы на своём срезе. NB:
535
+ > latency меряется с тачки где запущен MCP, а НЕ с РФ-relay-ноды; для
536
+ > гео-релевантной задержки используй `xray_test_reality_live` прямо с
537
+ > ноды (или ssh + curl на ней).
322
538
 
323
539
  Также один MCP **ресурс**: `xray://docs/index`.
324
540
 
@@ -500,7 +716,7 @@ $env:GITHUB_TOKEN = "ghp_xxx" # PowerShell
500
716
 
501
717
  ## Roadmap
502
718
 
503
- См. [ROADMAP.md](./ROADMAP.md) — все вехи v0.1–v0.10 закрыты.
719
+ См. [ROADMAP.md](./ROADMAP.md) — все вехи v0.1–v0.11 закрыты.
504
720
 
505
721
  ## Лицензия
506
722