network-terminal-mcp 0.1.0__tar.gz

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 (53) hide show
  1. network_terminal_mcp-0.1.0/.github/workflows/ci.yml +32 -0
  2. network_terminal_mcp-0.1.0/.github/workflows/release.yml +55 -0
  3. network_terminal_mcp-0.1.0/.gitignore +17 -0
  4. network_terminal_mcp-0.1.0/AGENTS.md +60 -0
  5. network_terminal_mcp-0.1.0/CHANGELOG.md +49 -0
  6. network_terminal_mcp-0.1.0/LICENSE +21 -0
  7. network_terminal_mcp-0.1.0/PKG-INFO +169 -0
  8. network_terminal_mcp-0.1.0/README.md +136 -0
  9. network_terminal_mcp-0.1.0/docs/adapters.md +41 -0
  10. network_terminal_mcp-0.1.0/docs/architecture.md +173 -0
  11. network_terminal_mcp-0.1.0/docs/configuration.md +171 -0
  12. network_terminal_mcp-0.1.0/docs/development-plan.md +44 -0
  13. network_terminal_mcp-0.1.0/docs/history.md +274 -0
  14. network_terminal_mcp-0.1.0/docs/open-questions.md +46 -0
  15. network_terminal_mcp-0.1.0/docs/operations.md +206 -0
  16. network_terminal_mcp-0.1.0/docs/security.md +160 -0
  17. network_terminal_mcp-0.1.0/docs/testing.md +114 -0
  18. network_terminal_mcp-0.1.0/docs/validation.md +136 -0
  19. network_terminal_mcp-0.1.0/pyproject.toml +79 -0
  20. network_terminal_mcp-0.1.0/src/network_terminal_mcp/__init__.py +3 -0
  21. network_terminal_mcp-0.1.0/src/network_terminal_mcp/__main__.py +44 -0
  22. network_terminal_mcp-0.1.0/src/network_terminal_mcp/audit.py +81 -0
  23. network_terminal_mcp-0.1.0/src/network_terminal_mcp/config/__init__.py +36 -0
  24. network_terminal_mcp-0.1.0/src/network_terminal_mcp/config/loader.py +78 -0
  25. network_terminal_mcp-0.1.0/src/network_terminal_mcp/config/models.py +282 -0
  26. network_terminal_mcp-0.1.0/src/network_terminal_mcp/connections/__init__.py +1 -0
  27. network_terminal_mcp-0.1.0/src/network_terminal_mcp/connections/plan.py +116 -0
  28. network_terminal_mcp-0.1.0/src/network_terminal_mcp/credentials/__init__.py +9 -0
  29. network_terminal_mcp-0.1.0/src/network_terminal_mcp/credentials/pass_backend.py +110 -0
  30. network_terminal_mcp-0.1.0/src/network_terminal_mcp/errors.py +67 -0
  31. network_terminal_mcp-0.1.0/src/network_terminal_mcp/host_keys.py +188 -0
  32. network_terminal_mcp-0.1.0/src/network_terminal_mcp/redaction.py +64 -0
  33. network_terminal_mcp-0.1.0/src/network_terminal_mcp/server.py +175 -0
  34. network_terminal_mcp-0.1.0/src/network_terminal_mcp/sessions/__init__.py +21 -0
  35. network_terminal_mcp-0.1.0/src/network_terminal_mcp/sessions/manager.py +920 -0
  36. network_terminal_mcp-0.1.0/src/network_terminal_mcp/sessions/models.py +77 -0
  37. network_terminal_mcp-0.1.0/src/network_terminal_mcp/socks.py +108 -0
  38. network_terminal_mcp-0.1.0/src/network_terminal_mcp/terminal.py +494 -0
  39. network_terminal_mcp-0.1.0/src/network_terminal_mcp/usage.md +134 -0
  40. network_terminal_mcp-0.1.0/src/network_terminal_mcp/usage.py +45 -0
  41. network_terminal_mcp-0.1.0/tests/test_audit.py +81 -0
  42. network_terminal_mcp-0.1.0/tests/test_config.py +339 -0
  43. network_terminal_mcp-0.1.0/tests/test_connections.py +108 -0
  44. network_terminal_mcp-0.1.0/tests/test_errors.py +42 -0
  45. network_terminal_mcp-0.1.0/tests/test_host_keys.py +154 -0
  46. network_terminal_mcp-0.1.0/tests/test_main.py +61 -0
  47. network_terminal_mcp-0.1.0/tests/test_pass_backend.py +181 -0
  48. network_terminal_mcp-0.1.0/tests/test_redaction.py +61 -0
  49. network_terminal_mcp-0.1.0/tests/test_server.py +234 -0
  50. network_terminal_mcp-0.1.0/tests/test_sessions.py +936 -0
  51. network_terminal_mcp-0.1.0/tests/test_socks.py +102 -0
  52. network_terminal_mcp-0.1.0/tests/test_terminal.py +180 -0
  53. network_terminal_mcp-0.1.0/uv.lock +1413 -0
@@ -0,0 +1,32 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ checks:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ python-version: ["3.12", "3.13", "3.14"]
19
+ steps:
20
+ - uses: actions/checkout@v7
21
+ - uses: astral-sh/setup-uv@v10.2.0
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+ enable-cache: true
25
+ - name: Sync environment
26
+ run: uv sync --locked
27
+ - name: Ruff
28
+ run: uv run ruff check .
29
+ - name: Mypy
30
+ run: uv run mypy src
31
+ - name: Pytest
32
+ run: uv run pytest
@@ -0,0 +1,55 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v7
15
+ - uses: astral-sh/setup-uv@v10.2.0
16
+ with:
17
+ enable-cache: true
18
+ - name: Build sdist and wheel
19
+ run: uv build
20
+ - uses: actions/upload-artifact@v7
21
+ with:
22
+ name: dist
23
+ path: dist/
24
+
25
+ publish:
26
+ needs: build
27
+ runs-on: ubuntu-latest
28
+ environment: pypi
29
+ permissions:
30
+ id-token: write
31
+ steps:
32
+ - uses: actions/download-artifact@v8
33
+ with:
34
+ name: dist
35
+ path: dist/
36
+ - name: Publish to PyPI
37
+ uses: pypa/gh-action-pypi-publish@release/v1
38
+ with:
39
+ packages-dir: dist/
40
+
41
+ github-release:
42
+ needs: [build, publish]
43
+ runs-on: ubuntu-latest
44
+ permissions:
45
+ contents: write
46
+ steps:
47
+ - uses: actions/checkout@v7
48
+ - uses: actions/download-artifact@v8
49
+ with:
50
+ name: dist
51
+ path: dist/
52
+ - name: Create GitHub Release
53
+ env:
54
+ GH_TOKEN: ${{ github.token }}
55
+ run: gh release create "${GITHUB_REF_NAME}" dist/* --generate-notes --verify-tag
@@ -0,0 +1,17 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ .coverage
9
+ htmlcov/
10
+ dist/
11
+ build/
12
+
13
+ # Local and secret-bearing runtime configuration
14
+ .env
15
+ *.log
16
+ transcripts/
17
+ outputs/
@@ -0,0 +1,60 @@
1
+ # Repository Guide
2
+
3
+ ## Read First
4
+
5
+ - `README.md` defines the project scope.
6
+ - `docs/architecture.md` is the technical source of truth.
7
+ - `docs/development-plan.md` lists ordered milestones and exit criteria.
8
+ - `docs/security.md` contains mandatory safety requirements.
9
+ - `src/network_terminal_mcp/usage.md` is the model-facing manual shipped as MCP
10
+ `instructions` + the `network-terminal://usage` resource; keep it in sync with
11
+ `usage.py` and the OpenCode skill.
12
+ - There are no example config files: connections are model-described inline. Never commit real
13
+ addresses, hostnames, or secrets anywhere in the repository.
14
+
15
+ ## Safety Rules
16
+
17
+ - Never add plaintext passwords, TACACS secrets, enable secrets, API tokens, or real production
18
+ addresses to this repository.
19
+ - Never run integration tests against real network devices without explicit user approval for the
20
+ exact targets and commands.
21
+ - Sessions are raw interactive terminals: the model may type any command, including configuration
22
+ entry. The safety boundary is the client permission gate on `open_session` (and optionally on
23
+ `terminal_write`), the audited input, and the operator's AAA policy — not command allowlists.
24
+ - Secrets typed at a live prompt must go through `terminal_write_secret` (a `pass` entry reference);
25
+ its value must never appear in tool arguments, results, or audit. Plain `terminal_write` is
26
+ logged verbatim and must not be used for secrets.
27
+ - Keep legacy SSH algorithms scoped to explicit per-call host allowlists. Never weaken global
28
+ SSH settings.
29
+ - Telnet, TCP console, and local serial must be explicitly enabled per call (`allow_telnet=true`,
30
+ `allow_serial=true`) and clearly marked insecure; policy can hard-deny them.
31
+ - Audit failures are fail-closed: if an action cannot be recorded, it must not be executed.
32
+ - Secret retrieval is internal to the server. MCP tool arguments and results must not contain
33
+ passwords, except an explicit plaintext opt-in behind `allow_plaintext_password`, which is never
34
+ stored, never audited, and always marked insecure. References (`pass` entry, key file) are the
35
+ default.
36
+
37
+ ## Engineering Rules
38
+
39
+ - Use Python 3.12+ and `uv`.
40
+ - Terminal transport is implemented directly on Paramiko (SSH), telnetlib3
41
+ (Telnet/TCP console), and pyserial (local serial) in
42
+ `src/network_terminal_mcp/terminal.py`. Do not reintroduce a vendor-driver
43
+ abstraction or nested connection routes; the model identifies the device type
44
+ from output and performs further ssh/telnet hops itself with `terminal_write`.
45
+ - Keep command knowledge out of the transport layer. The transport handles
46
+ writes, reads, prompt detection, and timeouts only.
47
+ - Validate the connection spec and policy with Pydantic before opening any
48
+ network connection.
49
+ - Use structured errors and redact secrets before logging or returning failures.
50
+
51
+ ## Planned Verification
52
+
53
+ ```bash
54
+ uv run ruff check .
55
+ uv run mypy src
56
+ uv run pytest
57
+ ```
58
+
59
+ Add these checks as the corresponding implementation appears. Do not add placeholder tests that
60
+ assert no behavior.
@@ -0,0 +1,49 @@
1
+ # Changelog
2
+
3
+ Все значимые изменения проекта фиксируются в этом файле. Формат основан на
4
+ [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/), версии следуют
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-10-05
10
+
11
+ Первый публичный релиз. Сессия — один постоянный терминальный stream; модель
12
+ пишет в него точный ввод, включая переходы `ssh`/`telnet`, и читает вывод без
13
+ требования определённой формы prompt.
14
+
15
+ ### Added
16
+
17
+ - Сырые терминальные инструменты: `open_session`, `terminal_write`,
18
+ `terminal_read`, `terminal_write_secret`, `read_output`, `session_status`,
19
+ `close_session`.
20
+ - `terminal_write_secret` вводит значение `pass` в живой prompt, не раскрывая
21
+ его в аргументах, результатах и audit (логируются только entry и размер).
22
+ - Транспорты SSH (Paramiko), Telnet/TCP console (telnetlib3) и локальный
23
+ serial `/dev/tty*` (pyserial); Telnet/console/serial включаются явными
24
+ per-call флагами `allow_telnet` / `allow_serial`.
25
+ - Маршруты первого подключения: direct, локальный SOCKS5 (`socks`) и SSH
26
+ ProxyJump (`proxyjump`).
27
+ - Модель-ориентированная инструкция внутри MCP: `instructions` в initialize и
28
+ ресурс `network-terminal://usage` (исходник
29
+ `src/network_terminal_mcp/usage.md`).
30
+ - Необязательный `policy.yml` (posture и лимиты), per-call allowlist legacy
31
+ SSH-алгоритмов, JSONL-audit и mutable redaction.
32
+ - Лимиты runtime: idle/lifetime сессий, размеры чтения/записи/буфера вывода,
33
+ максимальное число одновременных сессий.
34
+
35
+ ### Changed
36
+
37
+ - Сессии: несколько независимых сессий в одном процессе, вывод отдаётся без
38
+ требования prompt, prompt detection — best-effort.
39
+ - Секреты: ссылки `pass`/key file по умолчанию; plaintext-пароль — только за
40
+ явным `allow_plaintext_password` и помечается как insecure.
41
+ - Документация переписана под raw terminal; история этапов 0-8 вынесена в
42
+ `docs/history.md`.
43
+
44
+ ### Removed
45
+
46
+ - Командно-ориентированные инструменты `run_command`, `run_commands`,
47
+ `cli_help`, `send_control`, `respond`, `run_change` и command policy.
48
+ - `nested`-маршрут: вложенные переходы выполняются вводом в сессии.
49
+ - Инвентарь и файлы `connections.yml`, `credentials.yml`, `inventory.yml`.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Evgeny Zhuravlev
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.
@@ -0,0 +1,169 @@
1
+ Metadata-Version: 2.5
2
+ Name: network-terminal-mcp
3
+ Version: 0.1.0
4
+ Summary: Session-oriented MCP server for interactive network device terminals
5
+ Project-URL: Homepage, https://github.com/evgenyzh/network-terminal-mcp
6
+ Project-URL: Repository, https://github.com/evgenyzh/network-terminal-mcp
7
+ Project-URL: Issues, https://github.com/evgenyzh/network-terminal-mcp/issues
8
+ Project-URL: Changelog, https://github.com/evgenyzh/network-terminal-mcp/blob/main/CHANGELOG.md
9
+ Author: Evgeny Zhuravlev
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: mcp,network,serial,ssh,telnet,terminal
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: Intended Audience :: Telecommunications Industry
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: System :: Networking
23
+ Classifier: Topic :: System :: Systems Administration
24
+ Requires-Python: >=3.12
25
+ Requires-Dist: mcp<3,>=2.1.1
26
+ Requires-Dist: paramiko<5,>=4.0
27
+ Requires-Dist: pydantic-settings>=2.10
28
+ Requires-Dist: pydantic>=2.12
29
+ Requires-Dist: pyserial>=3.5
30
+ Requires-Dist: pyyaml>=6.0
31
+ Requires-Dist: telnetlib3<6,>=5.0
32
+ Description-Content-Type: text/markdown
33
+
34
+ # network-terminal-mcp
35
+
36
+ [![PyPI](https://img.shields.io/pypi/v/network-terminal-mcp)](https://pypi.org/project/network-terminal-mcp/)
37
+ [![CI](https://github.com/evgenyzh/network-terminal-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/evgenyzh/network-terminal-mcp/actions/workflows/ci.yml)
38
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
39
+
40
+ Локальный MCP-сервер для постоянных интерактивных сессий с сетевым
41
+ оборудованием. Проект даёт OpenCode сырой терминал: подключиться к устройству,
42
+ использовать контекстную подсказку `?`, выполнить несколько команд, при
43
+ необходимости зайти вторым `ssh`/`telnet` внутрь той же сессии и получить полный
44
+ вывод без временных `sshpass`-команд и одноразовых скриптов.
45
+
46
+ Статус: v0.1.0 — этапы 1-9 реализованы. Сессия — это один постоянный терминальный stream
47
+ (SSH, Telnet, TCP console или локальный serial `/dev/tty*`); модель пишет в него
48
+ точно то, что нужно, включая вложенные переходы, и читает вывод без требования
49
+ определённой формы prompt. Для первого подключения поддерживаются direct, один
50
+ локальный SOCKS5 hop и один SSH ProxyJump hop; `nested`-маршрутов и
51
+ командно-ориентированных инструментов больше нет. Секреты, запрашиваемые уже
52
+ внутри сессии, вводятся через `terminal_write_secret` со ссылкой на `pass` и не
53
+ попадают в audit. Один процесс держит несколько независимых сессий.
54
+
55
+ Подключение описывает модель в самом вызове `open_session`: host, protocol,
56
+ credentials (ссылки `pass`/key file или plaintext за флагом), route
57
+ (direct/socks/proxyjump), host key policy, serial-параметры и legacy-алгоритмы.
58
+ Инвентаря и profile-конфигов больше нет — после установки достаточно открыть
59
+ сессию. Единственный необязательный локальный файл — `policy.yml` (posture и
60
+ лимиты). Проверено на живом оборудовании: direct SSH на Cisco IOS, SNR
61
+ old/eNOS, D-Link, Huawei VRP и Junos; ProxyJump через реальные bastion.
62
+ Подробности в [результатах проверок](docs/validation.md).
63
+
64
+ Текущие ограничения: raw input выполняется без per-команды подтверждения —
65
+ после одобренного `open_session` модель работает в устройстве свободно; оператор
66
+ может добавить permission `ask` для `terminal_write` в OpenCode. Автоматического
67
+ распознавания sensitive-команд (`conf t`, `system-view`, `commit`) пока нет.
68
+ Telnet, console и serial требуют явных per-call флагов и могут быть hard-deny
69
+ политикой. `transcripts_enabled` остаётся зарезервированной настройкой.
70
+
71
+ ## Основные цели
72
+
73
+ - Прямой SSH, SOCKS5, ProxyJump, Telnet, TCP console и локальный serial.
74
+ - Современное и устаревшее оборудование: legacy SSH алгоритмы включаются явно
75
+ для конкретного host в вызове.
76
+ - Постоянная сессия: авторизация выполняется один раз, затем модель пишет
77
+ команды, `ssh`/`telnet` и одиночные клавиши в тот же stream.
78
+ - Несколько параллельных сессий в одном процессе: переключение между
79
+ устройствами без переподключения.
80
+ - Zero-config: модель описывает соединение сама, локально нужен только
81
+ необязательный `policy.yml`.
82
+ - Точные команды выбирает модель. MCP не переводит абстрактные операции в
83
+ vendor CLI и не хранит полный каталог команд.
84
+ - Собственный терминальный слой на Paramiko, telnetlib3 и pyserial; тип
85
+ устройства модель определяет сама по баннеру и выводу.
86
+ - Пароли загружаются из `pass` или явного key file; секреты вводятся в живой
87
+ prompt через `terminal_write_secret` и не попадают в MCP arguments, results и
88
+ audit. Plaintext-пароль — только за явным insecure-флагом.
89
+ - Все подключения, ввод и события терминала журналируются без секретов.
90
+
91
+
92
+ ## Первая область поддержки
93
+
94
+ - Cisco IOS/IOS-XE, включая старые 29xx/35xx.
95
+ - Huawei VRP и Huawei OLT.
96
+ - Juniper Junos.
97
+ - SNR 29xx и 52xx на базе механики Cisco IOS, но как разные CLI-диалекты.
98
+ - D-Link DGS/DES.
99
+ - Eltex MES/ESR.
100
+ - MikroTik RouterOS через обычный SSH.
101
+ - BDCOM, EcoSGE и PON-платформы через generic transport.
102
+
103
+ ## Не входит в первую версию
104
+
105
+ - Отдельный RouterOS API MCP.
106
+ - Полноценная система управления конфигурациями или Source of Truth.
107
+ - Автоматическая запись в production.
108
+ - Обход TACACS/RADIUS command authorization.
109
+ - Автоматическое включение слабых SSH-алгоритмов для всех устройств.
110
+
111
+ ## Документы
112
+
113
+ - [Инструкция для модели](src/network_terminal_mcp/usage.md) — она же MCP-ресурс
114
+ `network-terminal://usage`; краткий контракт едет в MCP `instructions`
115
+ - [Архитектура](docs/architecture.md)
116
+ - [План разработки](docs/development-plan.md)
117
+ - [Модель безопасности](docs/security.md)
118
+ - [Конфигурация](docs/configuration.md)
119
+ - [Эксплуатация](docs/operations.md)
120
+ - [Результаты проверок](docs/validation.md)
121
+ - [История этапов 0-8](docs/history.md)
122
+ - [Разработка адаптеров](docs/adapters.md)
123
+ - [Стратегия тестирования](docs/testing.md)
124
+ - [Открытые вопросы](docs/open-questions.md)
125
+
126
+ ## Установка
127
+
128
+ ```bash
129
+ uvx network-terminal-mcp
130
+ # или как постоянный инструмент:
131
+ uv tool install network-terminal-mcp
132
+ # или:
133
+ pip install network-terminal-mcp
134
+ ```
135
+
136
+ ## Запуск
137
+
138
+ Локальный MCP запускается OpenCode через `stdio`, без прослушивания TCP-порта:
139
+
140
+ ```json
141
+ {
142
+ "mcp": {
143
+ "network-terminal": {
144
+ "type": "local",
145
+ "command": ["uvx", "network-terminal-mcp"],
146
+ "enabled": true
147
+ }
148
+ },
149
+ "permission": {
150
+ "network-terminal_open_session": "ask"
151
+ }
152
+ }
153
+ ```
154
+
155
+ Конфигурационные файлы не обязательны. Для строгих ограничений (например,
156
+ hard-deny Telnet, serial, legacy-алгоритмов, plaintext) можно положить
157
+ `policy.yml` в `~/.config/network-terminal-mcp/`. Ввод в живой сессии по
158
+ умолчанию не подтверждается: после одобренного `open_session` модель работает в
159
+ терминале свободно.
160
+
161
+ Проверка локальной политики до запуска:
162
+
163
+ ```bash
164
+ uv sync
165
+ uv run python -m network_terminal_mcp check
166
+ ```
167
+
168
+ Подробный порядок регистрации SSH host key и запуска через OpenCode описан в
169
+ [руководстве эксплуатации](docs/operations.md).
@@ -0,0 +1,136 @@
1
+ # network-terminal-mcp
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/network-terminal-mcp)](https://pypi.org/project/network-terminal-mcp/)
4
+ [![CI](https://github.com/evgenyzh/network-terminal-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/evgenyzh/network-terminal-mcp/actions/workflows/ci.yml)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+
7
+ Локальный MCP-сервер для постоянных интерактивных сессий с сетевым
8
+ оборудованием. Проект даёт OpenCode сырой терминал: подключиться к устройству,
9
+ использовать контекстную подсказку `?`, выполнить несколько команд, при
10
+ необходимости зайти вторым `ssh`/`telnet` внутрь той же сессии и получить полный
11
+ вывод без временных `sshpass`-команд и одноразовых скриптов.
12
+
13
+ Статус: v0.1.0 — этапы 1-9 реализованы. Сессия — это один постоянный терминальный stream
14
+ (SSH, Telnet, TCP console или локальный serial `/dev/tty*`); модель пишет в него
15
+ точно то, что нужно, включая вложенные переходы, и читает вывод без требования
16
+ определённой формы prompt. Для первого подключения поддерживаются direct, один
17
+ локальный SOCKS5 hop и один SSH ProxyJump hop; `nested`-маршрутов и
18
+ командно-ориентированных инструментов больше нет. Секреты, запрашиваемые уже
19
+ внутри сессии, вводятся через `terminal_write_secret` со ссылкой на `pass` и не
20
+ попадают в audit. Один процесс держит несколько независимых сессий.
21
+
22
+ Подключение описывает модель в самом вызове `open_session`: host, protocol,
23
+ credentials (ссылки `pass`/key file или plaintext за флагом), route
24
+ (direct/socks/proxyjump), host key policy, serial-параметры и legacy-алгоритмы.
25
+ Инвентаря и profile-конфигов больше нет — после установки достаточно открыть
26
+ сессию. Единственный необязательный локальный файл — `policy.yml` (posture и
27
+ лимиты). Проверено на живом оборудовании: direct SSH на Cisco IOS, SNR
28
+ old/eNOS, D-Link, Huawei VRP и Junos; ProxyJump через реальные bastion.
29
+ Подробности в [результатах проверок](docs/validation.md).
30
+
31
+ Текущие ограничения: raw input выполняется без per-команды подтверждения —
32
+ после одобренного `open_session` модель работает в устройстве свободно; оператор
33
+ может добавить permission `ask` для `terminal_write` в OpenCode. Автоматического
34
+ распознавания sensitive-команд (`conf t`, `system-view`, `commit`) пока нет.
35
+ Telnet, console и serial требуют явных per-call флагов и могут быть hard-deny
36
+ политикой. `transcripts_enabled` остаётся зарезервированной настройкой.
37
+
38
+ ## Основные цели
39
+
40
+ - Прямой SSH, SOCKS5, ProxyJump, Telnet, TCP console и локальный serial.
41
+ - Современное и устаревшее оборудование: legacy SSH алгоритмы включаются явно
42
+ для конкретного host в вызове.
43
+ - Постоянная сессия: авторизация выполняется один раз, затем модель пишет
44
+ команды, `ssh`/`telnet` и одиночные клавиши в тот же stream.
45
+ - Несколько параллельных сессий в одном процессе: переключение между
46
+ устройствами без переподключения.
47
+ - Zero-config: модель описывает соединение сама, локально нужен только
48
+ необязательный `policy.yml`.
49
+ - Точные команды выбирает модель. MCP не переводит абстрактные операции в
50
+ vendor CLI и не хранит полный каталог команд.
51
+ - Собственный терминальный слой на Paramiko, telnetlib3 и pyserial; тип
52
+ устройства модель определяет сама по баннеру и выводу.
53
+ - Пароли загружаются из `pass` или явного key file; секреты вводятся в живой
54
+ prompt через `terminal_write_secret` и не попадают в MCP arguments, results и
55
+ audit. Plaintext-пароль — только за явным insecure-флагом.
56
+ - Все подключения, ввод и события терминала журналируются без секретов.
57
+
58
+
59
+ ## Первая область поддержки
60
+
61
+ - Cisco IOS/IOS-XE, включая старые 29xx/35xx.
62
+ - Huawei VRP и Huawei OLT.
63
+ - Juniper Junos.
64
+ - SNR 29xx и 52xx на базе механики Cisco IOS, но как разные CLI-диалекты.
65
+ - D-Link DGS/DES.
66
+ - Eltex MES/ESR.
67
+ - MikroTik RouterOS через обычный SSH.
68
+ - BDCOM, EcoSGE и PON-платформы через generic transport.
69
+
70
+ ## Не входит в первую версию
71
+
72
+ - Отдельный RouterOS API MCP.
73
+ - Полноценная система управления конфигурациями или Source of Truth.
74
+ - Автоматическая запись в production.
75
+ - Обход TACACS/RADIUS command authorization.
76
+ - Автоматическое включение слабых SSH-алгоритмов для всех устройств.
77
+
78
+ ## Документы
79
+
80
+ - [Инструкция для модели](src/network_terminal_mcp/usage.md) — она же MCP-ресурс
81
+ `network-terminal://usage`; краткий контракт едет в MCP `instructions`
82
+ - [Архитектура](docs/architecture.md)
83
+ - [План разработки](docs/development-plan.md)
84
+ - [Модель безопасности](docs/security.md)
85
+ - [Конфигурация](docs/configuration.md)
86
+ - [Эксплуатация](docs/operations.md)
87
+ - [Результаты проверок](docs/validation.md)
88
+ - [История этапов 0-8](docs/history.md)
89
+ - [Разработка адаптеров](docs/adapters.md)
90
+ - [Стратегия тестирования](docs/testing.md)
91
+ - [Открытые вопросы](docs/open-questions.md)
92
+
93
+ ## Установка
94
+
95
+ ```bash
96
+ uvx network-terminal-mcp
97
+ # или как постоянный инструмент:
98
+ uv tool install network-terminal-mcp
99
+ # или:
100
+ pip install network-terminal-mcp
101
+ ```
102
+
103
+ ## Запуск
104
+
105
+ Локальный MCP запускается OpenCode через `stdio`, без прослушивания TCP-порта:
106
+
107
+ ```json
108
+ {
109
+ "mcp": {
110
+ "network-terminal": {
111
+ "type": "local",
112
+ "command": ["uvx", "network-terminal-mcp"],
113
+ "enabled": true
114
+ }
115
+ },
116
+ "permission": {
117
+ "network-terminal_open_session": "ask"
118
+ }
119
+ }
120
+ ```
121
+
122
+ Конфигурационные файлы не обязательны. Для строгих ограничений (например,
123
+ hard-deny Telnet, serial, legacy-алгоритмов, plaintext) можно положить
124
+ `policy.yml` в `~/.config/network-terminal-mcp/`. Ввод в живой сессии по
125
+ умолчанию не подтверждается: после одобренного `open_session` модель работает в
126
+ терминале свободно.
127
+
128
+ Проверка локальной политики до запуска:
129
+
130
+ ```bash
131
+ uv sync
132
+ uv run python -m network_terminal_mcp check
133
+ ```
134
+
135
+ Подробный порядок регистрации SSH host key и запуска через OpenCode описан в
136
+ [руководстве эксплуатации](docs/operations.md).
@@ -0,0 +1,41 @@
1
+ # Транспорт и определение типа устройства
2
+
3
+ ## Нет слоя адаптеров
4
+
5
+ Проект не имеет адаптерного или драйверного слоя. Транспорт унифицирован для
6
+ любых ssh/telnet/console/serial-устройств и реализован в
7
+ `src/network_terminal_mcp/terminal.py`:
8
+
9
+ - SSH — Paramiko;
10
+ - Telnet и TCP console — telnetlib3;
11
+ - локальный serial — pyserial.
12
+
13
+ Понятия `platform`, `dialect`, `driver`, `generic_termserver` и `redispatch`
14
+ отсутствуют. Нет ни поля `platform`, ни инструмента `set_platform`.
15
+
16
+ ## Как определяется тип устройства
17
+
18
+ `open_session` не принимает аргумент `platform` (и вообще не имеет дела с
19
+ конфигурационными профилями: соединение описывается inline). Модель определяет
20
+ семейство устройства по баннеру и первичному выводу, а затем взаимодействует
21
+ через сырой терминал:
22
+
23
+ - `terminal_write` — команды, `?`-подсказки, одиночные клавиши, `ssh`/`telnet`;
24
+ - `terminal_read` — чтение вывода без требования prompt;
25
+ - `terminal_write_secret` — секрет из `pass` на живой prompt.
26
+
27
+ ## Механика терминала
28
+
29
+ Различия между устройствами (paging, help, очистка строки, confirmation
30
+ prompts, login prompts следующего hop) — это просто вывод и ввод в одном
31
+ stream. Сервер не классифицирует его и не переводит сессию в особые состояния:
32
+ модель читает то, что пришло, и отвечает тем, что нужно. Vendor-специфичного
33
+ кода нет: `terminal_write`/`terminal_read` работают одинаково для любого
34
+ устройства.
35
+
36
+ ## Специфичное поведение
37
+
38
+ Если позже потребуется специфичное для устройства поведение, его место — в
39
+ универсальном терминальном слое (`terminal.py`) или в skill, а не в
40
+ vendor-драйвере. Такое поведение должно проверяться на transcript'ах и не
41
+ дублироваться для каждого вендора.