zapret2-mcp 0.3.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 (76) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/LICENSE +21 -0
  3. package/README.md +238 -0
  4. package/build/executor/docker.d.ts +7 -0
  5. package/build/executor/docker.js +22 -0
  6. package/build/executor/docker.js.map +1 -0
  7. package/build/executor/factory.d.ts +2 -0
  8. package/build/executor/factory.js +27 -0
  9. package/build/executor/factory.js.map +1 -0
  10. package/build/executor/index.d.ts +6 -0
  11. package/build/executor/index.js +5 -0
  12. package/build/executor/index.js.map +1 -0
  13. package/build/executor/local.d.ts +5 -0
  14. package/build/executor/local.js +17 -0
  15. package/build/executor/local.js.map +1 -0
  16. package/build/executor/ssh.d.ts +13 -0
  17. package/build/executor/ssh.js +33 -0
  18. package/build/executor/ssh.js.map +1 -0
  19. package/build/executor/types.d.ts +8 -0
  20. package/build/executor/types.js +2 -0
  21. package/build/executor/types.js.map +1 -0
  22. package/build/executorInstance.d.ts +3 -0
  23. package/build/executorInstance.js +11 -0
  24. package/build/executorInstance.js.map +1 -0
  25. package/build/index.d.ts +2 -0
  26. package/build/index.js +75 -0
  27. package/build/index.js.map +1 -0
  28. package/build/logStore.d.ts +12 -0
  29. package/build/logStore.js +76 -0
  30. package/build/logStore.js.map +1 -0
  31. package/build/prompts.d.ts +2 -0
  32. package/build/prompts.js +156 -0
  33. package/build/prompts.js.map +1 -0
  34. package/build/resources.d.ts +2 -0
  35. package/build/resources.js +41 -0
  36. package/build/resources.js.map +1 -0
  37. package/build/tools/checkPrerequisites.d.ts +19 -0
  38. package/build/tools/checkPrerequisites.js +116 -0
  39. package/build/tools/checkPrerequisites.js.map +1 -0
  40. package/build/tools/configureDns.d.ts +35 -0
  41. package/build/tools/configureDns.js +106 -0
  42. package/build/tools/configureDns.js.map +1 -0
  43. package/build/tools/createSystemdService.d.ts +27 -0
  44. package/build/tools/createSystemdService.js +74 -0
  45. package/build/tools/createSystemdService.js.map +1 -0
  46. package/build/tools/detectSystem.d.ts +19 -0
  47. package/build/tools/detectSystem.js +89 -0
  48. package/build/tools/detectSystem.js.map +1 -0
  49. package/build/tools/getConfig.d.ts +27 -0
  50. package/build/tools/getConfig.js +30 -0
  51. package/build/tools/getConfig.js.map +1 -0
  52. package/build/tools/getStatus.d.ts +19 -0
  53. package/build/tools/getStatus.js +35 -0
  54. package/build/tools/getStatus.js.map +1 -0
  55. package/build/tools/installZapret.d.ts +31 -0
  56. package/build/tools/installZapret.js +84 -0
  57. package/build/tools/installZapret.js.map +1 -0
  58. package/build/tools/restartService.d.ts +19 -0
  59. package/build/tools/restartService.js +24 -0
  60. package/build/tools/restartService.js.map +1 -0
  61. package/build/tools/runBlockcheck.d.ts +31 -0
  62. package/build/tools/runBlockcheck.js +44 -0
  63. package/build/tools/runBlockcheck.js.map +1 -0
  64. package/build/tools/startService.d.ts +19 -0
  65. package/build/tools/startService.js +24 -0
  66. package/build/tools/startService.js.map +1 -0
  67. package/build/tools/stopService.d.ts +19 -0
  68. package/build/tools/stopService.js +24 -0
  69. package/build/tools/stopService.js.map +1 -0
  70. package/build/tools/updateConfig.d.ts +31 -0
  71. package/build/tools/updateConfig.js +50 -0
  72. package/build/tools/updateConfig.js.map +1 -0
  73. package/build/tools/verifyBypass.d.ts +31 -0
  74. package/build/tools/verifyBypass.js +62 -0
  75. package/build/tools/verifyBypass.js.map +1 -0
  76. package/package.json +64 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ ## [0.3.0] - 2026-02-18
4
+
5
+ ### Added
6
+ - **3 новых tools** (итого 13):
7
+ - `detectSystem` — определение окружения (OS, arch, init system, WAN, DNS, NFQUEUE, container)
8
+ - `configureDns` — настройка DNS-резолвера (resolv.conf или systemd-resolved)
9
+ - `createSystemdService` — создание systemd unit для автозапуска на Linux-десктопе
10
+ - **Новый prompt** `setup-desktop` — полный пайплайн установки для Linux-десктопа с systemd
11
+ - Автодетекция WAN-интерфейса через `ip route get 8.8.8.8` при установке (`installZapret`)
12
+ - Расширенные проверки в `checkPrerequisites`: OS, init system, NFQUEUE module, WAN-интерфейс, DNS-резолверы, container detection, `base64`
13
+ - Документация по поддержке Windows (`docs/windows-support.md`)
14
+
15
+ ### Fixed
16
+ - **Shell injection в `updateConfig`** — значение теперь передаётся через base64 + awk ENVIRON вместо sed с минимальным экранированием
17
+ - **maxBuffer 1MB → 10MB** во всех executor'ах — предотвращает обрезку вывода blockcheck2.sh
18
+ - Опечатка в `docker/entrypoint.sh` (`/opt/zapret` → `/opt/zapret2`)
19
+
20
+ ### Changed
21
+ - Промпт `setup-zapret` начинается с `detectSystem` как шаг 1
22
+ - Промпт `troubleshoot` включает `detectSystem` и `configureDns`
23
+ - Промпт `overview` обновлён: 13 tools, 5 prompts, два workflow (router/desktop)
24
+ - README.md полностью переписан на русском с таблицей поддерживаемых платформ
25
+
26
+ ## [0.2.0] - 2026-02-17
27
+
28
+ ### Added
29
+ - **Абстракция транспорта** `CommandExecutor` — 3 реализации:
30
+ - `LocalExecutor` — выполнение через `bash -c`
31
+ - `DockerExecutor` — через `docker exec`
32
+ - `SshExecutor` — через ssh
33
+ - **3 новых tools** (итого 10):
34
+ - `checkPrerequisites` — проверка окружения
35
+ - `installZapret` — установка с нуля
36
+ - `verifyBypass` — проверка сетевой связности
37
+ - **MCP Resources** — персистентные логи в `~/.zapret2-mcp/logs/`
38
+ - **4 MCP Prompts**: `setup-zapret`, `find-bypass-strategy`, `troubleshoot`, `overview`
39
+ - npm-пакет с CLI entry point (`npx zapret2-mcp`)
40
+ - Unit и интеграционные тесты
41
+
42
+ ### Changed
43
+ - Все tools мигрированы с `dockerExec()` на `getExecutor().exec()`
44
+
45
+ ## [0.1.0] - 2026-02-17
46
+
47
+ ### Added
48
+ - Начальная реализация MCP-сервера с 7 tools
49
+ - Docker-окружение для разработки (OpenWrt rootfs)
50
+ - Tools: `getStatus`, `startService`, `stopService`, `restartService`, `getConfig`, `updateConfig`, `runBlockcheck`
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stanislav Zemlyakov
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,238 @@
1
+ # zapret2-mcp
2
+
3
+ MCP-сервер для управления [zapret2](https://github.com/bol-van/zapret2) — инструментом обработки сетевых пакетов. Позволяет AI-агентам автоматизировать установку, настройку и диагностику zapret2 через [Model Context Protocol](https://modelcontextprotocol.io/).
4
+
5
+ ## Поддерживаемые платформы
6
+
7
+ | Платформа | Статус | Транспорт |
8
+ |---|---|---|
9
+ | OpenWrt (роутер) | Полная поддержка | `ssh` |
10
+ | Linux (десктоп/сервер) | Полная поддержка | `local` |
11
+ | Docker (разработка/тестирование) | Полная поддержка | `docker` |
12
+ | macOS | Не поддерживается | — |
13
+ | Windows (нативно) | Не поддерживается | — |
14
+ | Windows (WSL2) | Работает через `local` | `local` |
15
+
16
+ zapret2 поддерживает Windows нативно через [zapret-win-bundle](https://github.com/bol-van/zapret-win-bundle), но архитектура (WinDivert, `.cmd`-скрипты, другие пути) полностью отличается от Linux. Наш MCP-сервер работает только с Linux/OpenWrt-окружениями. Подробнее: [docs/windows-support.md](docs/windows-support.md).
17
+
18
+ ## Установка
19
+
20
+ ```bash
21
+ npm install -g zapret2-mcp
22
+ ```
23
+
24
+ Или запуск без установки:
25
+
26
+ ```bash
27
+ npx zapret2-mcp
28
+ ```
29
+
30
+ ## Настройка MCP-клиента
31
+
32
+ ### Claude Desktop
33
+
34
+ Добавить в `~/.claude/claude_desktop_config.json`:
35
+
36
+ ```json
37
+ {
38
+ "mcpServers": {
39
+ "zapret2": {
40
+ "command": "npx",
41
+ "args": ["zapret2-mcp"],
42
+ "env": {
43
+ "ZAPRET2_MODE": "ssh",
44
+ "ZAPRET2_SSH_HOST": "192.168.1.1"
45
+ }
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ### Claude Code
52
+
53
+ Добавить в `.mcp.json` в корне проекта или в `~/.claude/mcp.json` глобально:
54
+
55
+ ```json
56
+ {
57
+ "mcpServers": {
58
+ "zapret2": {
59
+ "command": "npx",
60
+ "args": ["zapret2-mcp"],
61
+ "env": {
62
+ "ZAPRET2_MODE": "ssh",
63
+ "ZAPRET2_SSH_HOST": "192.168.1.1"
64
+ }
65
+ }
66
+ }
67
+ }
68
+ ```
69
+
70
+ ### Cursor
71
+
72
+ Добавить в настройки MCP:
73
+
74
+ ```json
75
+ {
76
+ "zapret2": {
77
+ "command": "npx",
78
+ "args": ["zapret2-mcp"],
79
+ "env": {
80
+ "ZAPRET2_MODE": "docker"
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ ## Режимы транспорта
87
+
88
+ Сервер выполняет команды на целевой машине через один из трёх транспортов.
89
+
90
+ ### `ssh` — удалённый роутер (продакшн)
91
+
92
+ Подключение по SSH к OpenWrt-роутеру. Основной режим для реального использования.
93
+
94
+ ```json
95
+ { "ZAPRET2_MODE": "ssh", "ZAPRET2_SSH_HOST": "192.168.1.1" }
96
+ ```
97
+
98
+ ### `local` — локальная машина
99
+
100
+ Выполнение команд через `bash -c` на той же машине, где запущен MCP-сервер. Для Linux-десктопов и WSL2.
101
+
102
+ ```json
103
+ { "ZAPRET2_MODE": "local" }
104
+ ```
105
+
106
+ ### `docker` — контейнер (по умолчанию)
107
+
108
+ Выполнение через `docker exec` в контейнере с OpenWrt. Для разработки и тестирования.
109
+
110
+ ```json
111
+ { "ZAPRET2_MODE": "docker" }
112
+ ```
113
+
114
+ ### Переменные окружения
115
+
116
+ | Переменная | По умолчанию | Описание |
117
+ |---|---|---|
118
+ | `ZAPRET2_MODE` | `docker` | Транспорт: `local`, `docker`, `ssh` |
119
+ | `ZAPRET2_CONTAINER_NAME` | `zapret2-openwrt` | Имя контейнера (режим docker) |
120
+ | `ZAPRET2_SSH_HOST` | — (обязательно для ssh) | SSH-хост |
121
+ | `ZAPRET2_SSH_USER` | `root` | SSH-пользователь |
122
+ | `ZAPRET2_SSH_KEY` | — (опционально) | Путь к SSH-ключу |
123
+ | `ZAPRET2_SSH_PORT` | `22` | SSH-порт |
124
+
125
+ ## Tools (13)
126
+
127
+ ### Определение системы
128
+
129
+ | Tool | Описание |
130
+ |---|---|
131
+ | `detectSystem` | Определение окружения: ОС, архитектура, init-система, WAN-интерфейс, DNS, NFQUEUE, контейнер |
132
+
133
+ ### Управление сервисом
134
+
135
+ | Tool | Описание |
136
+ |---|---|
137
+ | `getStatus` | Статус сервиса: запущен ли, PID, количество nftables-правил, флаг enabled |
138
+ | `startService` | Запуск zapret2 (логи сохраняются в resources) |
139
+ | `stopService` | Остановка zapret2 (логи сохраняются в resources) |
140
+ | `restartService` | Перезапуск zapret2 (логи сохраняются в resources) |
141
+
142
+ ### Конфигурация
143
+
144
+ | Tool | Описание |
145
+ |---|---|
146
+ | `getConfig` | Чтение конфига zapret2 (целиком или по ключу) |
147
+ | `updateConfig` | Обновление параметра конфига (key=value, снапшот сохраняется в resources) |
148
+ | `configureDns` | Настройка DNS-резолвера (resolv.conf или systemd-resolved) |
149
+
150
+ ### Установка и диагностика
151
+
152
+ | Tool | Описание |
153
+ |---|---|
154
+ | `checkPrerequisites` | Проверка окружения: инструменты, ОС, init-система, NFQUEUE, сеть |
155
+ | `installZapret` | Полная установка: клонирование, скачивание бинарников, базовый конфиг |
156
+ | `runBlockcheck` | Запуск blockcheck2.sh для поиска рабочих сетевых стратегий (~5 мин, лог в resources) |
157
+ | `verifyBypass` | Проверка сетевой связности: DNS, HTTP, статус nfqws2 |
158
+
159
+ ### Интеграция с десктопом
160
+
161
+ | Tool | Описание |
162
+ |---|---|
163
+ | `createSystemdService` | Создание systemd unit для автозапуска zapret2 на Linux-десктопе |
164
+
165
+ ## Prompts (5)
166
+
167
+ MCP-промпты — пошаговые инструкции для типичных сценариев:
168
+
169
+ | Prompt | Описание |
170
+ |---|---|
171
+ | `setup-zapret` | Установка с нуля (универсальная) |
172
+ | `setup-desktop` | Установка на Linux-десктоп с systemd, DNS и автозапуском |
173
+ | `find-bypass-strategy` | Поиск рабочей сетевой стратегии через blockcheck |
174
+ | `troubleshoot` | Диагностика проблем |
175
+ | `overview` | Справочник по всем tools, resources и workflows |
176
+
177
+ ## MCP Resources
178
+
179
+ Tools сохраняют вывод в файлы для персистентной истории. Агент может возвращаться к предыдущим результатам и сравнивать прогоны.
180
+
181
+ - **URI:** `zapret2://logs/{type}/{timestamp}`
182
+ - **Типы логов:** `blockcheck`, `service`, `config`
183
+ - **Хранение:** `~/.zapret2-mcp/logs/`
184
+
185
+ ## Сценарии использования
186
+
187
+ ### Роутер (OpenWrt по SSH)
188
+
189
+ ```
190
+ detectSystem → checkPrerequisites → installZapret → updateConfig(NFQWS2_ENABLE=1)
191
+ → startService → verifyBypass
192
+ ```
193
+
194
+ ### Linux-десктоп
195
+
196
+ ```
197
+ detectSystem → checkPrerequisites → installZapret → configureDns
198
+ → createSystemdService → updateConfig(NFQWS2_ENABLE=1) → startService → verifyBypass
199
+ ```
200
+
201
+ ### Поиск сетевой стратегии
202
+
203
+ ```
204
+ stopService → runBlockcheck(domain) → прочитать лог resource
205
+ → updateConfig(NFQWS2_OPT=...) → restartService → verifyBypass(domain)
206
+ ```
207
+
208
+ ### Диагностика
209
+
210
+ ```
211
+ detectSystem → getStatus → getConfig → checkPrerequisites → verifyBypass(domain)
212
+ → анализ результатов
213
+ ```
214
+
215
+ ## Разработка
216
+
217
+ ```bash
218
+ git clone https://github.com/your-org/zapret2-mcp.git
219
+ cd zapret2-mcp
220
+ npm install
221
+ npm run build # TypeScript → build/
222
+ npm run dev # MCP Inspector
223
+ npm test # Unit-тесты (vitest)
224
+ npm run test:integration # Интеграционные тесты (требуется Docker)
225
+ ```
226
+
227
+ ### Docker-окружение для разработки
228
+
229
+ ```bash
230
+ cd docker
231
+ docker compose build && docker compose up -d
232
+ ```
233
+
234
+ Контейнер `zapret2-openwrt`: OpenWrt SNAPSHOT, `privileged: true`, `network_mode: host`.
235
+
236
+ ## Лицензия
237
+
238
+ MIT
@@ -0,0 +1,7 @@
1
+ import type { CommandExecutor, ExecResult } from "./types.js";
2
+ export declare class DockerExecutor implements CommandExecutor {
3
+ readonly label: string;
4
+ private containerName;
5
+ constructor(containerName?: string);
6
+ exec(command: string, timeoutMs?: number): Promise<ExecResult>;
7
+ }
@@ -0,0 +1,22 @@
1
+ import { execFile } from "child_process";
2
+ export class DockerExecutor {
3
+ label;
4
+ containerName;
5
+ constructor(containerName = "zapret2-openwrt") {
6
+ this.containerName = containerName;
7
+ this.label = `docker:${containerName}`;
8
+ }
9
+ exec(command, timeoutMs = 30000) {
10
+ return new Promise((resolve, reject) => {
11
+ execFile("docker", ["exec", this.containerName, "bash", "-c", command], { timeout: timeoutMs, maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
12
+ if (error) {
13
+ reject({ error, stdout, stderr });
14
+ }
15
+ else {
16
+ resolve({ stdout, stderr });
17
+ }
18
+ });
19
+ });
20
+ }
21
+ }
22
+ //# sourceMappingURL=docker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker.js","sourceRoot":"","sources":["../../src/executor/docker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,MAAM,OAAO,cAAc;IAChB,KAAK,CAAS;IACf,aAAa,CAAS;IAE9B,YAAY,aAAa,GAAG,iBAAiB;QAC3C,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,KAAK,GAAG,UAAU,aAAa,EAAE,CAAC;IACzC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,SAAS,GAAG,KAAK;QACrC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,QAAQ,CACN,QAAQ,EACR,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,EACnD,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,EACnD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;gBACxB,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;gBACpC,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ import type { CommandExecutor } from "./types.js";
2
+ export declare function createExecutor(): CommandExecutor;
@@ -0,0 +1,27 @@
1
+ import { LocalExecutor } from "./local.js";
2
+ import { DockerExecutor } from "./docker.js";
3
+ import { SshExecutor } from "./ssh.js";
4
+ export function createExecutor() {
5
+ const mode = (process.env.ZAPRET2_MODE || "docker").toLowerCase();
6
+ switch (mode) {
7
+ case "local":
8
+ return new LocalExecutor();
9
+ case "docker":
10
+ return new DockerExecutor(process.env.ZAPRET2_CONTAINER_NAME || "zapret2-openwrt");
11
+ case "ssh": {
12
+ const host = process.env.ZAPRET2_SSH_HOST;
13
+ if (!host) {
14
+ throw new Error("ZAPRET2_SSH_HOST is required when ZAPRET2_MODE=ssh");
15
+ }
16
+ return new SshExecutor({
17
+ host,
18
+ user: process.env.ZAPRET2_SSH_USER || "root",
19
+ port: process.env.ZAPRET2_SSH_PORT ? Number(process.env.ZAPRET2_SSH_PORT) : 22,
20
+ keyPath: process.env.ZAPRET2_SSH_KEY || undefined,
21
+ });
22
+ }
23
+ default:
24
+ throw new Error(`Unknown ZAPRET2_MODE: ${mode}. Must be local, docker, or ssh`);
25
+ }
26
+ }
27
+ //# sourceMappingURL=factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/executor/factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC,MAAM,UAAU,cAAc;IAC5B,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IAElE,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,OAAO;YACV,OAAO,IAAI,aAAa,EAAE,CAAC;QAE7B,KAAK,QAAQ;YACX,OAAO,IAAI,cAAc,CACvB,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,iBAAiB,CACxD,CAAC;QAEJ,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;YAC1C,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;YACxE,CAAC;YACD,OAAO,IAAI,WAAW,CAAC;gBACrB,IAAI;gBACJ,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,MAAM;gBAC5C,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,EAAE;gBAC9E,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,SAAS;aAClD,CAAC,CAAC;QACL,CAAC;QAED;YACE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,iCAAiC,CAAC,CAAC;IACpF,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ export type { CommandExecutor, ExecResult } from "./types.js";
2
+ export { LocalExecutor } from "./local.js";
3
+ export { DockerExecutor } from "./docker.js";
4
+ export { SshExecutor } from "./ssh.js";
5
+ export type { SshOptions } from "./ssh.js";
6
+ export { createExecutor } from "./factory.js";
@@ -0,0 +1,5 @@
1
+ export { LocalExecutor } from "./local.js";
2
+ export { DockerExecutor } from "./docker.js";
3
+ export { SshExecutor } from "./ssh.js";
4
+ export { createExecutor } from "./factory.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/executor/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { CommandExecutor, ExecResult } from "./types.js";
2
+ export declare class LocalExecutor implements CommandExecutor {
3
+ readonly label = "local";
4
+ exec(command: string, timeoutMs?: number): Promise<ExecResult>;
5
+ }
@@ -0,0 +1,17 @@
1
+ import { execFile } from "child_process";
2
+ export class LocalExecutor {
3
+ label = "local";
4
+ exec(command, timeoutMs = 30000) {
5
+ return new Promise((resolve, reject) => {
6
+ execFile("bash", ["-c", command], { timeout: timeoutMs, maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
7
+ if (error) {
8
+ reject({ error, stdout, stderr });
9
+ }
10
+ else {
11
+ resolve({ stdout, stderr });
12
+ }
13
+ });
14
+ });
15
+ }
16
+ }
17
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/executor/local.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,MAAM,OAAO,aAAa;IACf,KAAK,GAAG,OAAO,CAAC;IAEzB,IAAI,CAAC,OAAe,EAAE,SAAS,GAAG,KAAK;QACrC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,QAAQ,CACN,MAAM,EACN,CAAC,IAAI,EAAE,OAAO,CAAC,EACf,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,EACnD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;gBACxB,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;gBACpC,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ import type { CommandExecutor, ExecResult } from "./types.js";
2
+ export interface SshOptions {
3
+ host: string;
4
+ user?: string;
5
+ port?: number;
6
+ keyPath?: string;
7
+ }
8
+ export declare class SshExecutor implements CommandExecutor {
9
+ readonly label: string;
10
+ private sshArgs;
11
+ constructor(options: SshOptions);
12
+ exec(command: string, timeoutMs?: number): Promise<ExecResult>;
13
+ }
@@ -0,0 +1,33 @@
1
+ import { execFile } from "child_process";
2
+ export class SshExecutor {
3
+ label;
4
+ sshArgs;
5
+ constructor(options) {
6
+ const user = options.user || "root";
7
+ const port = options.port || 22;
8
+ this.label = `ssh:${user}@${options.host}:${port}`;
9
+ this.sshArgs = [
10
+ "-o", "BatchMode=yes",
11
+ "-o", "StrictHostKeyChecking=accept-new",
12
+ "-o", "ConnectTimeout=10",
13
+ "-p", String(port),
14
+ ];
15
+ if (options.keyPath) {
16
+ this.sshArgs.push("-i", options.keyPath);
17
+ }
18
+ this.sshArgs.push(`${user}@${options.host}`);
19
+ }
20
+ exec(command, timeoutMs = 30000) {
21
+ return new Promise((resolve, reject) => {
22
+ execFile("ssh", [...this.sshArgs, command], { timeout: timeoutMs, maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
23
+ if (error) {
24
+ reject({ error, stdout, stderr });
25
+ }
26
+ else {
27
+ resolve({ stdout, stderr });
28
+ }
29
+ });
30
+ });
31
+ }
32
+ }
33
+ //# sourceMappingURL=ssh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh.js","sourceRoot":"","sources":["../../src/executor/ssh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAUzC,MAAM,OAAO,WAAW;IACb,KAAK,CAAS;IACf,OAAO,CAAW;IAE1B,YAAY,OAAmB;QAC7B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC;QACpC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;QAChC,IAAI,CAAC,KAAK,GAAG,OAAO,IAAI,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC;QAEnD,IAAI,CAAC,OAAO,GAAG;YACb,IAAI,EAAE,eAAe;YACrB,IAAI,EAAE,kCAAkC;YACxC,IAAI,EAAE,mBAAmB;YACzB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;SACnB,CAAC;QAEF,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,SAAS,GAAG,KAAK;QACrC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,QAAQ,CACN,KAAK,EACL,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,EAC1B,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,EACnD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;gBACxB,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;gBACpC,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ export interface ExecResult {
2
+ stdout: string;
3
+ stderr: string;
4
+ }
5
+ export interface CommandExecutor {
6
+ exec(command: string, timeoutMs?: number): Promise<ExecResult>;
7
+ readonly label: string;
8
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/executor/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ import type { CommandExecutor } from "./executor/types.js";
2
+ export declare function initExecutor(executor: CommandExecutor): void;
3
+ export declare function getExecutor(): CommandExecutor;
@@ -0,0 +1,11 @@
1
+ let instance = null;
2
+ export function initExecutor(executor) {
3
+ instance = executor;
4
+ }
5
+ export function getExecutor() {
6
+ if (!instance) {
7
+ throw new Error("CommandExecutor not initialized. Call initExecutor() first.");
8
+ }
9
+ return instance;
10
+ }
11
+ //# sourceMappingURL=executorInstance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executorInstance.js","sourceRoot":"","sources":["../src/executorInstance.ts"],"names":[],"mappings":"AAEA,IAAI,QAAQ,GAA2B,IAAI,CAAC;AAE5C,MAAM,UAAU,YAAY,CAAC,QAAyB;IACpD,QAAQ,GAAG,QAAQ,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/build/index.js ADDED
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { createExecutor } from "./executor/index.js";
5
+ import { initExecutor } from "./executorInstance.js";
6
+ import { getStatusTool } from "./tools/getStatus.js";
7
+ import { startServiceTool } from "./tools/startService.js";
8
+ import { stopServiceTool } from "./tools/stopService.js";
9
+ import { restartServiceTool } from "./tools/restartService.js";
10
+ import { getConfigTool } from "./tools/getConfig.js";
11
+ import { updateConfigTool } from "./tools/updateConfig.js";
12
+ import { runBlockcheckTool } from "./tools/runBlockcheck.js";
13
+ import { checkPrerequisitesTool } from "./tools/checkPrerequisites.js";
14
+ import { installZapretTool } from "./tools/installZapret.js";
15
+ import { verifyBypassTool } from "./tools/verifyBypass.js";
16
+ import { detectSystemTool } from "./tools/detectSystem.js";
17
+ import { configureDnsTool } from "./tools/configureDns.js";
18
+ import { createSystemdServiceTool } from "./tools/createSystemdService.js";
19
+ import { registerResources } from "./resources.js";
20
+ import { registerPrompts } from "./prompts.js";
21
+ import { setOnLogSaved } from "./logStore.js";
22
+ const executor = createExecutor();
23
+ initExecutor(executor);
24
+ const server = new McpServer({
25
+ name: "zapret2-mcp",
26
+ version: "0.2.0",
27
+ }, {
28
+ instructions: `zapret2-mcp provides tools to install, configure, and diagnose zapret2 — a network packet processing tool for OpenWrt routers and Linux desktops.
29
+
30
+ Key workflows:
31
+ 1. Fresh install (router): checkPrerequisites → installZapret → updateConfig(NFQWS2_ENABLE=1) → startService → verifyBypass
32
+ 2. Fresh install (desktop): detectSystem → checkPrerequisites → installZapret → configureDns → createSystemdService → updateConfig(NFQWS2_ENABLE=1) → startService → verifyBypass
33
+ 3. Find bypass strategy: stopService → runBlockcheck(domain) → read log resource → updateConfig(NFQWS2_OPT=...) → restartService → verifyBypass(domain)
34
+ 4. Troubleshoot: detectSystem → getStatus → getConfig → checkPrerequisites → verifyBypass(domain) → analyze
35
+
36
+ Always start with detectSystem to determine the target environment (OS, init system, WAN interface).
37
+ Use configureDns to set up a reliable DNS resolver.
38
+ Use createSystemdService on systemd-based Linux desktops for service autostart.
39
+
40
+ Service management (start/stop/restart) saves logs to resources. updateConfig saves a config snapshot before changes.
41
+ runBlockcheck is a heavy operation (~5 min) that saves full output to resources for later analysis.
42
+ Use listResources to browse historical logs at zapret2://logs/{type}/{timestamp}.
43
+ Use getPrompt for step-by-step guidance: setup-zapret, setup-desktop, find-bypass-strategy, troubleshoot, overview.`,
44
+ });
45
+ const tools = [
46
+ getStatusTool,
47
+ startServiceTool,
48
+ stopServiceTool,
49
+ restartServiceTool,
50
+ getConfigTool,
51
+ updateConfigTool,
52
+ runBlockcheckTool,
53
+ checkPrerequisitesTool,
54
+ installZapretTool,
55
+ verifyBypassTool,
56
+ detectSystemTool,
57
+ configureDnsTool,
58
+ createSystemdServiceTool,
59
+ ];
60
+ for (const tool of tools) {
61
+ server.tool(tool.name, tool.description, tool.schema.shape, tool.handler);
62
+ }
63
+ registerResources(server);
64
+ registerPrompts(server);
65
+ setOnLogSaved(() => server.sendResourceListChanged());
66
+ async function main() {
67
+ const transport = new StdioServerTransport();
68
+ await server.connect(transport);
69
+ console.error(`zapret2-mcp server started (executor: ${executor.label})`);
70
+ }
71
+ main().catch((error) => {
72
+ console.error("Fatal error:", error);
73
+ process.exit(1);
74
+ });
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;AAClC,YAAY,CAAC,QAAQ,CAAC,CAAC;AAEvB,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B;IACE,IAAI,EAAE,aAAa;IACnB,OAAO,EAAE,OAAO;CACjB,EACD;IACE,YAAY,EAAE;;;;;;;;;;;;;;;oHAekG;CACjH,CACF,CAAC;AAEF,MAAM,KAAK,GAAG;IACZ,aAAa;IACb,gBAAgB;IAChB,eAAe;IACf,kBAAkB;IAClB,aAAa;IACb,gBAAgB;IAChB,iBAAiB;IACjB,sBAAsB;IACtB,iBAAiB;IACjB,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,wBAAwB;CACzB,CAAC;AAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;IACzB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;AAC5E,CAAC;AAED,iBAAiB,CAAC,MAAM,CAAC,CAAC;AAC1B,eAAe,CAAC,MAAM,CAAC,CAAC;AACxB,aAAa,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,uBAAuB,EAAE,CAAC,CAAC;AAEtD,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,yCAAyC,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC;AAC5E,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ export type LogType = "blockcheck" | "service" | "config";
2
+ export interface LogEntry {
3
+ type: LogType;
4
+ timestamp: string;
5
+ uri: string;
6
+ size: number;
7
+ meta: Record<string, string>;
8
+ }
9
+ export declare function setOnLogSaved(callback: () => void): void;
10
+ export declare function saveLog(type: LogType, content: string, meta?: Record<string, string>): string;
11
+ export declare function listLogs(type?: LogType): LogEntry[];
12
+ export declare function readLog(type: LogType, timestamp: string): string | null;