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.
- network_terminal_mcp-0.1.0/.github/workflows/ci.yml +32 -0
- network_terminal_mcp-0.1.0/.github/workflows/release.yml +55 -0
- network_terminal_mcp-0.1.0/.gitignore +17 -0
- network_terminal_mcp-0.1.0/AGENTS.md +60 -0
- network_terminal_mcp-0.1.0/CHANGELOG.md +49 -0
- network_terminal_mcp-0.1.0/LICENSE +21 -0
- network_terminal_mcp-0.1.0/PKG-INFO +169 -0
- network_terminal_mcp-0.1.0/README.md +136 -0
- network_terminal_mcp-0.1.0/docs/adapters.md +41 -0
- network_terminal_mcp-0.1.0/docs/architecture.md +173 -0
- network_terminal_mcp-0.1.0/docs/configuration.md +171 -0
- network_terminal_mcp-0.1.0/docs/development-plan.md +44 -0
- network_terminal_mcp-0.1.0/docs/history.md +274 -0
- network_terminal_mcp-0.1.0/docs/open-questions.md +46 -0
- network_terminal_mcp-0.1.0/docs/operations.md +206 -0
- network_terminal_mcp-0.1.0/docs/security.md +160 -0
- network_terminal_mcp-0.1.0/docs/testing.md +114 -0
- network_terminal_mcp-0.1.0/docs/validation.md +136 -0
- network_terminal_mcp-0.1.0/pyproject.toml +79 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/__init__.py +3 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/__main__.py +44 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/audit.py +81 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/config/__init__.py +36 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/config/loader.py +78 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/config/models.py +282 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/connections/__init__.py +1 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/connections/plan.py +116 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/credentials/__init__.py +9 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/credentials/pass_backend.py +110 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/errors.py +67 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/host_keys.py +188 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/redaction.py +64 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/server.py +175 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/sessions/__init__.py +21 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/sessions/manager.py +920 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/sessions/models.py +77 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/socks.py +108 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/terminal.py +494 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/usage.md +134 -0
- network_terminal_mcp-0.1.0/src/network_terminal_mcp/usage.py +45 -0
- network_terminal_mcp-0.1.0/tests/test_audit.py +81 -0
- network_terminal_mcp-0.1.0/tests/test_config.py +339 -0
- network_terminal_mcp-0.1.0/tests/test_connections.py +108 -0
- network_terminal_mcp-0.1.0/tests/test_errors.py +42 -0
- network_terminal_mcp-0.1.0/tests/test_host_keys.py +154 -0
- network_terminal_mcp-0.1.0/tests/test_main.py +61 -0
- network_terminal_mcp-0.1.0/tests/test_pass_backend.py +181 -0
- network_terminal_mcp-0.1.0/tests/test_redaction.py +61 -0
- network_terminal_mcp-0.1.0/tests/test_server.py +234 -0
- network_terminal_mcp-0.1.0/tests/test_sessions.py +936 -0
- network_terminal_mcp-0.1.0/tests/test_socks.py +102 -0
- network_terminal_mcp-0.1.0/tests/test_terminal.py +180 -0
- 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,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
|
+
[](https://pypi.org/project/network-terminal-mcp/)
|
|
37
|
+
[](https://github.com/evgenyzh/network-terminal-mcp/actions/workflows/ci.yml)
|
|
38
|
+
[](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
|
+
[](https://pypi.org/project/network-terminal-mcp/)
|
|
4
|
+
[](https://github.com/evgenyzh/network-terminal-mcp/actions/workflows/ci.yml)
|
|
5
|
+
[](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
|
+
дублироваться для каждого вендора.
|