mcp-xray-pilot
v0.14.0
Published
MCP server for xray-core: offline docs cache (auto-refresh), config validation, REALITY/XTLS lint, anti-DPI recipes, GitHub research from XTLS repos.
Downloads
499
Maintainers
Readme
mcp-xray-pilot
Русская версия ниже / Russian version below
A Model Context Protocol (MCP) server that gives an LLM offline access to the official xray-core documentation, plus deep structural validation, best-practice lint, a protocol/transport/security compatibility matrix, a full v2fly geosite catalogue (~1500 categories), an alternative-stack suggester, REALITY keypair / shortId generators, a live SNI target validator, a curated SNI suggester per exit-country and a multi-config merge helper.
What it does
- Bundles ~60 docs pages from the upstream
XTLS/Xray-docs-nextrepo as raw markdown (no html→md mess). - Refreshes on demand:
xray_fetch_topictries the network first and silently overwrites the bundled cache on success. If you're offline (or upstream is down), it falls back to the packaged copy and surfaces awarningfield. - Searches the corpus with a tiny title/body relevance scorer.
- Validates xray JSON config: required top-level fields, per-protocol
Zod schemas (vless / vmess / trojan / shadowsocks / socks / http /
wireguard / hysteria / freedom / blackhole / dns / loopback / dokodemo /
tun), per-transport
*Settingsschemas (raw / xhttp / grpc / ws / mkcp / httpupgrade / hysteria), TLS / REALITY security blocks, routing tag cross-references. - Lints ~22 best-practice rules: VLESS
decryption: "none", REALITY pubkey/shortId/target syntax, XTLS vision flow compatibility, TLS fingerprint enum, ALPN collisions, geosite/geoip typo catcher, protocol × transport × security incompatibilities,xhttp.pathleading slash,geoip:privateblock rule, sniffing on 80/443, DNS-over-proxy leaks on.rudirect routing (v0.12),geosite:categories absent from xray-core releasegeosite.dat(v0.12) etc. - Geo catalogue: search ~1500 known geoip/geosite tags by substring (full
v2fly/domain-list-community catalogue, hydrated from
data/geocatalogue.json). - REALITY toolbelt:
xray_generate_reality_keypair(drop-in replacement forxray x25519),xray_generate_short_ids(cryptographically random, legacy-empty prefix),xray_validate_sni_target(live TLS 1.3 + h2 probe),xray_suggest_sni_for_country(curated REALITY fronts per exit-country). - Compares protocols: side-by-side table of vless/vmess/trojan/ss/ hysteria2/wireguard on transports, security, anti-DPI, mobile, battery.
- Recommends a stack for a stated goal (anti-DPI in RU/IR/CN, low latency, mobile battery, high throughput, stealth-CDN, getting started).
- Merges configs: joins inbounds/outbounds/routing.rules from N JSON configs, auto-resolves tag collisions, warns on port collisions.
Tools
| Tool | What it does |
| -------------------------- | ------------------------------------------------------------------------------------- |
| xray_list_topics | List doc topics, grouped by category. Use first to discover slugs. |
| xray_fetch_topic | Fetch one topic as markdown. Network → fall back to bundled cache → update cache. |
| xray_search | Full-text search over all cached docs. Returns ranked hits + snippets. |
| xray_validate_config | Structural+schema validation of an xray JSON config (Zod under the hood). |
| xray_lint | ~22 best-practice lint rules. Returns issues with severity, rule id, JSON-pointer. |
| xray_geo_search | Substring search over the embedded geosite/geoip catalogue (~1500 tags). |
| xray_diff_protocols | Side-by-side feature table for two protocols. |
| xray_suggest_alternative | Recommend protocol+transport+security for a goal (anti-DPI / battery / latency / …). |
| xray_generate_short_ids | Cryptographically random REALITY shortIds (default [4,8,16] bytes, legacy empty prefix). |
| xray_generate_reality_keypair | Fresh REALITY X25519 keypair, base64url 43 chars — drop-in for xray x25519. |
| xray_validate_sni_target | Live TLS 1.3 + ALPN h2 + HTTP probe of a candidate REALITY target host. |
| 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. |
| 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. |
| 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). |
| xray_merge_configs | Merge N xray configs with tag-collision resolution and conflict warnings. |
| xray_github_search | Search issues/PRs/discussions across XTLS GitHub repos (Xray-core/REALITY/docs). |
| xray_github_fetch_issue | Fetch one issue/PR/discussion with full body + top comments. |
| 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. |
Total: 18 tools.
Quick toolbelt
Generate a REALITY keypair (server-side priv + client-side pub):
// xray_generate_reality_keypair {}
{
"privateKey": "MNCibkT-h5bCF6iknJG0rJdHdfUjT7VugHgSX9BRWUY",
"publicKey": "ffvf0eNwnLhgK-axt3rajJIAoHKv0rX4xkw5KyImn38",
"note": "Server: paste privateKey into inbound realitySettings.privateKey. Clients: paste publicKey into outbound realitySettings.publicKey. Format matches `xray x25519` output exactly."
}Live-check a candidate REALITY SNI:
// xray_validate_sni_target { "host": "www.onet.pl" }
{
"host": "www.onet.pl", "port": 443, "ok": true,
"tls_version": "TLSv1.3", "alpn": "h2", "http_status": 200,
"cert_subject": "www.onet.pl", "cert_san_count": 4,
"latency_ms": 312, "issues": []
}Live REALITY handshake (catches what xray_validate_sni_target cannot):
// xray_test_reality_live { "target_host": "2gis.ru" }
{
"ok": true,
"target": "2gis.ru:443",
"reality_handshake_complete": true,
"client_received_real_cert": false,
"http_probe_status": 200,
"latency_ms": 824,
"issues": [],
"used_keypair": { "privateKey": "...", "publicKey": "...", "shortId": "a1b2c3d4" },
"cached": false,
"cached_at": "2026-05-06T12:30:00.000Z"
}Verdicts are persisted to
data/reality-verdicts.json(LRU cap 50, TTL 24h, keyhost:port). Subsequent calls with the same target return instantly withcached: true. Passforce_refresh: trueto bypass the cache and rerun xray.
Compare a list of candidates in one shot — runs sequentially, each entry hits
the cache, results are sorted by ok desc, latency_ms asc:
// xray_test_reality_live {
// "multi_targets": ["2gis.ru", "yandex.ru", "vk.com", "mail.ru"]
// }
{
"results": [
{ "target": "2gis.ru:443", "ok": true, "latency_ms": 824, "cached": true, "cached_at": "..." },
{ "target": "mail.ru:443", "ok": true, "latency_ms": 1102, "cached": false, "cached_at": "..." },
{ "target": "yandex.ru:443", "ok": false, "latency_ms": 0, "cached": false, "cached_at": "...", "issues": ["client received a real certificate ..."] },
{ "target": "vk.com:443", "ok": false, "latency_ms": 0, "cached": false, "cached_at": "...", "issues": ["target unreachable"] }
],
"summary": { "ok_count": 2, "total": 4 }
}Pull a public RU-traffic whitelist and rank live SNI candidates for an inbound RU-relay node:
// xray_whitelist_sni_candidates { "max_candidates": 10 }
{
"source_url": "https://raw.githubusercontent.com/hxehex/russia-mobile-internet-whitelist/main/whitelist.txt",
"fetched_at": "2026-05-06T12:34:00.000Z",
"total_domains": 412,
"tested": 10,
"candidates": [
{ "host": "ya.ru", "ok": true, "tls_version": "TLSv1.3", "alpn": "h2", "http_status": 200, "latency_ms": 41, "cert_subject": "ya.ru", "issues": [] },
{ "host": "vk.com", "ok": true, "tls_version": "TLSv1.3", "alpn": "h2", "http_status": 200, "latency_ms": 78, "cert_subject": "*.vk.com", "issues": [] },
{ "host": "ozon.ru", "ok": false, "tls_version": "TLSv1.3", "alpn": "h2", "http_status": 200, "latency_ms": 134, "cert_subject": "*.ozon.ru", "issues": [] }
],
"summary": { "ok_count": 8, "failed_count": 2 },
"cache": { "used": false, "age_seconds": null },
"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"]
}The whitelist body is cached on disk in
data/whitelist-cache.json(default TTL 24h, override viacache_ttl_hours). Verdicts are NOT cached — every call re-probes its slice live so latency numbers stay fresh. NB: the probe runs from your laptop, not from the РФ relay; for geo-accurate latency, usexray_test_reality_livefrom the actual node (or ssh + curl on it).
It also exposes a single MCP resource: xray://docs/index — the raw
_index.json of cached topics.
Examples
Example 1 — lint a broken cascade config
Prompt:
Lint this xray config and tell me what's wrong.
{ "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"}}}], "outbounds":[{"tag":"out","protocol":"freedom"}], "routing":{"rules":[{"type":"field","outboundTag":"missing","domain":["geosite:tinkoff-bank"]}]} }
xray_lint response (excerpt):
{
"summary": { "error_count": 6, "warn_count": 5, "info_count": 2 },
"issues": [
{ "rule": "reality_pubkey_format", "severity": "error",
"path": "/inbounds/0/streamSettings/realitySettings/privateKey",
"message": "REALITY privateKey must be 43 base64url chars, got 'short'" },
{ "rule": "reality_shortid_format", "severity": "error",
"path": "/inbounds/0/streamSettings/realitySettings/shortIds/0",
"message": "shortIds[0] 'zz' must be hex (0..16 chars, even length)" },
{ "rule": "flow_requires_specific_transport", "severity": "error",
"path": "/inbounds/0/streamSettings/network",
"message": "flow=xtls-rprx-vision requires raw/tcp transport, got 'ws'" },
{ "rule": "routing_dangling_outbound", "severity": "error",
"path": "/routing/rules/0/outboundTag",
"message": "outboundTag 'missing' does not match any outbound tag" },
{ "rule": "tls_fingerprint_enum", "severity": "warn",
"path": "/inbounds/0/streamSettings/realitySettings/fingerprint",
"message": "'netscape' is not a known fingerprint (chrome/firefox/safari/ios/android/edge/360/qq/random/randomized)" },
{ "rule": "geo_unknown_category", "severity": "warn",
"path": "/routing/rules/0/domain/0",
"message": "geosite:tinkoff-bank is not in the bundled catalogue (typo? try geosite:category-ru)" }
]
}Example 2 — research RKN bypass via GitHub
Prompt:
Find recent xray issues about RKN/TSPU and suggest an anti-DPI stack for Russia.
Step 1 — xray_github_search:
// args
{ "query": "RKN", "type": "issue", "repo": "all", "sort": "updated" }
// response (excerpt)
{
"total_count": 47,
"items": [
{ "repo": "XTLS/Xray-core", "number": 5747, "state": "open",
"title": "REALITY blocked by TSPU after recent RKN update",
"comments": 38, "reactions": { "+1": 21 },
"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…" },
{ "repo": "XTLS/Xray-core", "number": 5332, "state": "closed",
"title": "RKN: shortId enumeration probe",
"comments": 22,
"snippet": "TSPU appears to brute-force shortIds. Recommendation: rotate, keep list >=4 entries, do not include empty string…" }
]
}Step 2 — xray_suggest_alternative:
// args
{ "goal": "anti-dpi-russia" }
// response (excerpt)
{
"recommendation": {
"protocol": "vless",
"transport": "xhttp",
"security": "reality",
"flow": null,
"rationale": [
"xhttp survives TSPU active probing better than raw/tcp+vision in 2025",
"REALITY hides SNI; pick a target popular in RU (yandex.com, mail.ru)",
"Rotate shortIds[] (>=4 entries, hex, no empty string)",
"Keep packet padding default; do not enable kcp on top of REALITY"
]
},
"see_also": ["xray_fetch_topic transports/xhttp", "xray_fetch_topic features/reality"]
}Install
npm i -g mcp-xray-pilotOr run from source:
git clone https://github.com/beekamai/mcp-xray-pilot.git
cd mcp-xray-pilot
npm install
npm run build
npm run fetch-docs # fills data/docs/ if you cloned without itAdd to Claude Code
claude mcp add xray-pilot --scope user -- npx -y mcp-xray-pilotWith a GitHub PAT (raises xray_github_* rate limit, enables discussions):
claude mcp add xray-pilot --scope user --env GITHUB_TOKEN=ghp_xxx -- npx -y mcp-xray-pilotOr, from a local clone:
claude mcp add xray-pilot --scope user -- node /absolute/path/to/mcp-xray-pilot/dist/index.jsAdd to Cursor / Windsurf / Cline
{
"mcpServers": {
"xray-pilot": {
"command": "npx",
"args": ["-y", "mcp-xray-pilot"]
}
}
}Offline cache vs. online refresh
The data/docs/ directory ships with the package. Each call to
xray_fetch_topic:
- If
force_offline=true→ reads only the packaged copy. - Otherwise → tries the upstream raw markdown URL (10s timeout). On HTTP
200, the response body replaces the on-disk markdown and the index
entry's
fetched_atis updated. Subsequent calls in the same process serve from in-memory cache. - On any network failure → falls back to the packaged copy, returning the
markdown plus
warning: "network fetch failed: …".
To refresh everything in bulk, run npm run fetch-docs -- --refresh. To
discover newly-added pages upstream without writing them: npm run
fetch-docs -- --discover.
Keeping cache fresh
There are three complementary ways to keep data/docs/ aligned with
upstream:
- On-demand per page — every
xray_fetch_topiccall already tries the network first and silently overwrites the on-disk copy on success. No action needed. - Bulk via MCP tool — call
xray_refresh_cachefrom the LLM:{ "scope": "stale", "max_age_days": 30 }(default) re-fetches only entries older than N days.{ "scope": "all" }re-fetches every page (~60).{ "scope": "category", "category": "transports" }restricts to one category.- Add
"discover": trueto also report slugs that exist upstream but are missing fromDOCS_CATALOGUEinsrc/docs.ts.
- CI weekly cron —
.github/workflows/refresh-docs.ymlrunsnpm run fetch-docs -- --refreshevery Monday 06:00 UTC and opens a PR if anything changed (also triggerable manually viaworkflow_dispatch).
Optional GITHUB_TOKEN env variable
xray_github_search and xray_github_fetch_issue work anonymously, but
the GitHub API caps unauthenticated requests at 60/hour. Setting
GITHUB_TOKEN to any classic or fine-grained PAT (no scopes needed for
public repos) raises the limit to 5000/hour and additionally enables
the discussions endpoint (GraphQL), which has no anonymous access.
export GITHUB_TOKEN=ghp_xxx # Linux / macOS
$env:GITHUB_TOKEN = "ghp_xxx" # PowerShellWhen X-RateLimit-Remaining drops below 10, the tool surfaces an inline
warning in the response.
Roadmap
See ROADMAP.md. All v0.1–v0.11 milestones are checked off.
License
MIT.
mcp-xray-pilot (RU)
MCP-сервер, дающий LLM офлайн-доступ к официальной документации xray-core, плюс глубокую валидацию по схемам, lint best-practice, матрицу совместимости протоколов/транспортов/security, каталог geosite/geoip, рекомендатор альтернативного стека и helper для слияния конфигов.
Что делает
- Упаковывает ~60 страниц документации из upstream-репозитория
XTLS/Xray-docs-nextкак raw markdown (без html→md мусора). - Обновляется по запросу:
xray_fetch_topicсначала идёт в сеть и при успехе молча перезаписывает упакованный кеш. Если оффлайн (или upstream лёг), возвращает упакованную копию и выставляетwarning. - Поиск по корпусу простым title/body relevance scorer.
- Валидирует xray JSON: обязательные top-level поля, per-protocol
Zod-схемы (vless / vmess / trojan / shadowsocks / socks / http /
wireguard / hysteria / freedom / blackhole / dns / loopback / dokodemo /
tun), per-transport
*Settings(raw / xhttp / grpc / ws / mkcp / httpupgrade / hysteria), security блоки TLS/REALITY, routing tag cross-references. - Lint ~22 правил: VLESS
decryption: "none", REALITY pubkey/shortId/ target syntax, XTLS vision flow compatibility, TLS fingerprint enum, ALPN collisions, geo typo catcher, protocol × transport × security несовместимости,xhttp.pathслеш,geoip:privateblock, sniffing на 80/443, утечка DNS через прокси ломает.rudirect routing (v0.12),geosite:категории, которых нет в xray-core releasegeosite.dat(v0.12) и т.д. - Geo catalogue: поиск по ~1500 известным geoip/geosite тегам (полный
v2fly/domain-list-community каталог, гидратируется из
data/geocatalogue.json). - REALITY toolbelt:
xray_generate_reality_keypair(drop-in заменаxray x25519),xray_generate_short_ids(криптослучайные, с empty-prefix для легаси),xray_validate_sni_target(live TLS 1.3 + h2 проба),xray_suggest_sni_for_country(курируемые REALITY-фронты по стране exit'а). - Сравнение протоколов: таблица vless/vmess/trojan/ss/hysteria2/ wireguard по transports, security, anti-DPI, mobile, battery.
- Рекомендует стек под цель (anti-DPI в РФ/Иране/КНР, low-latency, mobile-battery, high-throughput, stealth-CDN, getting started).
- Сливает конфиги: объединяет inbounds/outbounds/routing.rules из N JSON конфигов, авто-резолвит коллизии тегов, варнит на коллизиях портов.
Тулы
| Тул | Что делает |
| -------------------------- | --------------------------------------------------------------------------------------- |
| xray_list_topics | Список тем по категориям. Дёргать первым. |
| xray_fetch_topic | Получить тему как markdown. Сеть → fallback на кеш → обновление кеша. |
| xray_search | Полнотекстовый поиск по докам. Хиты + сниппеты. |
| xray_validate_config | Структурная валидация + Zod-схемы по протоколу/transport/security. |
| xray_lint | ~20 правил best-practice. Issues с severity, rule id, JSON-pointer. |
| xray_geo_search | Поиск по embedded каталогу geosite/geoip по подстроке (~1500 тегов). |
| xray_diff_protocols | Side-by-side таблица фич двух протоколов. |
| xray_suggest_alternative | Рекомендация protocol+transport+security под цель. |
| xray_generate_short_ids | Криптослучайные REALITY shortIds (default [4,8,16] байт, с empty-prefix для легаси). |
| xray_generate_reality_keypair | Свежая X25519 пара REALITY, base64url 43 chars — drop-in для xray x25519. |
| xray_validate_sni_target | Live проба TLS 1.3 + ALPN h2 + HTTP кандидата на REALITY target. |
| xray_test_reality_live | Поднимает локальную пару xray (server+client), реально гоняет REALITY handshake к target, probe HTTPS сквозь каскад. Строго сильнее xray_validate_sni_target. |
| xray_whitelist_sni_candidates | Тянет публичный whitelist РФ-трафика (default: hxehex/russia-mobile-internet-whitelist), TLS-валидирует топ-N как REALITY SNI-кандидаты. Сортировка ok desc, latency asc. |
| xray_suggest_sni_for_country | Курируемые REALITY-фронты по стране exit'а (DE/PL/NL/FR/LV/SE/FI/US/UK/JP/SG/AU/CA). |
| xray_merge_configs | Слить N конфигов с разрешением коллизий тегов. |
| xray_github_search | Поиск issues/PR/discussions по XTLS GitHub репозиториям. |
| xray_github_fetch_issue | Получить одну issue/PR/discussion с полным body + топ комментариев. |
| xray_refresh_cache | Bulk перезатяжка кеша доков (scope: all/stale/category). Опц. discover + refresh_geocatalogue: true (заодно перетянет v2fly категории). |
Всего: 18 тулов.
Quick toolbelt
Сгенерить REALITY keypair (priv для сервера, pub для клиентов):
// xray_generate_reality_keypair {}
{
"privateKey": "MNCibkT-h5bCF6iknJG0rJdHdfUjT7VugHgSX9BRWUY",
"publicKey": "ffvf0eNwnLhgK-axt3rajJIAoHKv0rX4xkw5KyImn38",
"note": "Format matches `xray x25519` output exactly."
}Live-проверить кандидата на REALITY SNI:
// xray_validate_sni_target { "host": "www.onet.pl" }
{
"host": "www.onet.pl", "port": 443, "ok": true,
"tls_version": "TLSv1.3", "alpn": "h2", "http_status": 200,
"cert_subject": "www.onet.pl", "cert_san_count": 4,
"latency_ms": 312, "issues": []
}⚠️ Проба идёт с локальной машины, где запущен
mcp-xray-pilot. Для решения «работает ли SNI из РФ» — гоняй с РФ-IP отдельно.
Реальный REALITY handshake (ловит то, что xray_validate_sni_target пропускает):
// xray_test_reality_live { "target_host": "2gis.ru" }
{
"ok": true,
"target": "2gis.ru:443",
"reality_handshake_complete": true,
"client_received_real_cert": false,
"http_probe_status": 200,
"latency_ms": 824,
"issues": [],
"used_keypair": { "privateKey": "...", "publicKey": "...", "shortId": "a1b2c3d4" },
"cached": false,
"cached_at": "2026-05-06T12:30:00.000Z"
}При первом вызове скачивает 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.
Вердикты пишутся на диск в
data/reality-verdicts.json(LRU 50, TTL 24h, ключhost:port). Повторный вызов на тот же таргет возвращается мгновенно сcached: true. Чтобы прогнать заново —force_refresh: true.
Сравнить пачку кандидатов одним вызовом — запускаются последовательно, каждый через кэш, сорт ok desc, latency_ms asc:
// xray_test_reality_live {
// "multi_targets": ["2gis.ru", "yandex.ru", "vk.com", "mail.ru"]
// }
{
"results": [
{ "target": "2gis.ru:443", "ok": true, "latency_ms": 824, "cached": true },
{ "target": "mail.ru:443", "ok": true, "latency_ms": 1102, "cached": false },
{ "target": "yandex.ru:443", "ok": false, "issues": ["client received a real certificate ..."] },
{ "target": "vk.com:443", "ok": false, "issues": ["target unreachable"] }
],
"summary": { "ok_count": 2, "total": 4 }
}Затянуть публичный whitelist РФ-трафика и проранжировать кандидатов на REALITY SNI для inbound РФ-relay-ноды:
// xray_whitelist_sni_candidates { "max_candidates": 10 }
{
"source_url": "https://raw.githubusercontent.com/hxehex/russia-mobile-internet-whitelist/main/whitelist.txt",
"fetched_at": "2026-05-06T12:34:00.000Z",
"total_domains": 412,
"tested": 10,
"candidates": [
{ "host": "ya.ru", "ok": true, "tls_version": "TLSv1.3", "alpn": "h2", "http_status": 200, "latency_ms": 41, "cert_subject": "ya.ru", "issues": [] },
{ "host": "vk.com", "ok": true, "tls_version": "TLSv1.3", "alpn": "h2", "http_status": 200, "latency_ms": 78, "cert_subject": "*.vk.com", "issues": [] }
],
"summary": { "ok_count": 8, "failed_count": 2 },
"cache": { "used": false, "age_seconds": null },
"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"]
}Тело whitelist кешируется на диск в
data/whitelist-cache.json(TTL по умолчанию 24h, переопределение черезcache_ttl_hours). Verdicts НЕ кешируются — каждый вызов делает свежие пробы на своём срезе. NB: latency меряется с тачки где запущен MCP, а НЕ с РФ-relay-ноды; для гео-релевантной задержки используйxray_test_reality_liveпрямо с ноды (или ssh + curl на ней).
Также один MCP ресурс: xray://docs/index.
Примеры
Пример 1 — линт сломанного каскадного конфига
Промпт:
Прогони линт по этому xray конфигу и скажи что не так.
{ "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"}}}], "outbounds":[{"tag":"out","protocol":"freedom"}], "routing":{"rules":[{"type":"field","outboundTag":"missing","domain":["geosite:tinkoff-bank"]}]} }
Ответ xray_lint (выжимка):
{
"summary": { "error_count": 6, "warn_count": 5, "info_count": 2 },
"issues": [
{ "rule": "reality_pubkey_format", "severity": "error",
"path": "/inbounds/0/streamSettings/realitySettings/privateKey",
"message": "REALITY privateKey must be 43 base64url chars, got 'short'" },
{ "rule": "reality_shortid_format", "severity": "error",
"path": "/inbounds/0/streamSettings/realitySettings/shortIds/0",
"message": "shortIds[0] 'zz' must be hex (0..16 chars, even length)" },
{ "rule": "flow_requires_specific_transport", "severity": "error",
"path": "/inbounds/0/streamSettings/network",
"message": "flow=xtls-rprx-vision requires raw/tcp transport, got 'ws'" },
{ "rule": "routing_dangling_outbound", "severity": "error",
"path": "/routing/rules/0/outboundTag",
"message": "outboundTag 'missing' does not match any outbound tag" },
{ "rule": "tls_fingerprint_enum", "severity": "warn",
"path": "/inbounds/0/streamSettings/realitySettings/fingerprint",
"message": "'netscape' is not a known fingerprint (chrome/firefox/safari/ios/android/edge/360/qq/random/randomized)" },
{ "rule": "geo_unknown_category", "severity": "warn",
"path": "/routing/rules/0/domain/0",
"message": "geosite:tinkoff-bank is not in the bundled catalogue (typo? try geosite:category-ru)" }
]
}Пример 2 — ресёрч обхода РКН через GitHub
Промпт:
Найди свежие xray issues про РКН/ТСПУ и предложи anti-DPI стек под Россию.
Шаг 1 — xray_github_search:
// args
{ "query": "RKN", "type": "issue", "repo": "all", "sort": "updated" }
// response (выжимка)
{
"total_count": 47,
"items": [
{ "repo": "XTLS/Xray-core", "number": 5747, "state": "open",
"title": "REALITY blocked by TSPU after recent RKN update",
"comments": 38, "reactions": { "+1": 21 },
"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…" },
{ "repo": "XTLS/Xray-core", "number": 5332, "state": "closed",
"title": "RKN: shortId enumeration probe",
"comments": 22,
"snippet": "TSPU appears to brute-force shortIds. Recommendation: rotate, keep list >=4 entries, do not include empty string…" }
]
}Шаг 2 — xray_suggest_alternative:
// args
{ "goal": "anti-dpi-russia" }
// response (выжимка)
{
"recommendation": {
"protocol": "vless",
"transport": "xhttp",
"security": "reality",
"flow": null,
"rationale": [
"xhttp survives TSPU active probing better than raw/tcp+vision in 2025",
"REALITY hides SNI; pick a target popular in RU (yandex.com, mail.ru)",
"Rotate shortIds[] (>=4 entries, hex, no empty string)",
"Keep packet padding default; do not enable kcp on top of REALITY"
]
},
"see_also": ["xray_fetch_topic transports/xhttp", "xray_fetch_topic features/reality"]
}Установка
npm i -g mcp-xray-pilotИли из исходников:
git clone https://github.com/beekamai/mcp-xray-pilot.git
cd mcp-xray-pilot
npm install
npm run build
npm run fetch-docsПодключить к Claude Code
claude mcp add xray-pilot --scope user -- npx -y mcp-xray-pilotС GitHub PAT (поднимает rate-limit xray_github_*, включает discussions):
claude mcp add xray-pilot --scope user --env GITHUB_TOKEN=ghp_xxx -- npx -y mcp-xray-pilotИли из локального клона:
claude mcp add xray-pilot --scope user -- node /absolute/path/to/mcp-xray-pilot/dist/index.jsОфлайн-кеш vs онлайн-обновление
Папка data/docs/ едет в пакете. Каждый вызов xray_fetch_topic:
- При
force_offline=true→ читает только упакованную копию. - Иначе → пробует upstream raw URL (10s таймаут). При HTTP 200 ответ
перезаписывает markdown на диске,
fetched_atобновляется. Последующие вызовы в том же процессе отдаются из in-memory кеша. - При любой сетевой ошибке → fallback на упакованную копию, возвращает
markdown плюс
warning: "network fetch failed: …".
Bulk-обновление — npm run fetch-docs -- --refresh. Discover новых
страниц в upstream без записи — npm run fetch-docs -- --discover.
Поддержание кеша актуальным
Три способа держать data/docs/ в синхроне с upstream:
- На каждый запрос —
xray_fetch_topicсам ходит в сеть и при HTTP 200 перезаписывает on-disk копию. Ничего делать не надо. - Bulk через MCP-тул — позови
xray_refresh_cache:{ "scope": "stale", "max_age_days": 30 }(default) — только устаревшие старше N дней.{ "scope": "all" }— все ~60 страниц.{ "scope": "category", "category": "transports" }— одна категория."discover": true— дополнительно вернёт список slug'ов, которые появились upstream, но отсутствуют вDOCS_CATALOGUE(src/docs.ts).
- CI weekly cron —
.github/workflows/refresh-docs.ymlкаждый понедельник 06:00 UTC гоняетnpm run fetch-docs -- --refreshи открывает PR если что-то поменялось (естьworkflow_dispatchдля ручного триггера).
Опциональный GITHUB_TOKEN
xray_github_search и xray_github_fetch_issue работают анонимно, но
GitHub API лимитирует unauth запросы 60/час. Установка GITHUB_TOKEN
(любой classic или fine-grained PAT, для публичных репо scope не нужен)
поднимает лимит до 5000/час и дополнительно включает поиск/чтение
discussions (GraphQL, у него нет anon-доступа).
export GITHUB_TOKEN=ghp_xxx # Linux / macOS
$env:GITHUB_TOKEN = "ghp_xxx" # PowerShellКогда X-RateLimit-Remaining падает ниже 10, тул возвращает inline-warning
в ответе.
Roadmap
См. ROADMAP.md — все вехи v0.1–v0.11 закрыты.
Лицензия
MIT.
