remote-debug-mcp 0.2.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.
- remote_debug_mcp-0.2.0/AGENTS.md +94 -0
- remote_debug_mcp-0.2.0/DESIGN.md +501 -0
- remote_debug_mcp-0.2.0/LICENSE +21 -0
- remote_debug_mcp-0.2.0/PKG-INFO +315 -0
- remote_debug_mcp-0.2.0/README.md +272 -0
- remote_debug_mcp-0.2.0/config.example.yaml +17 -0
- remote_debug_mcp-0.2.0/pyproject.toml +40 -0
- remote_debug_mcp-0.2.0/src/remote/com2telnet.py +618 -0
- remote_debug_mcp-0.2.0/src/remote/pyproject.toml +18 -0
- remote_debug_mcp-0.2.0/src/remote_debug_mcp/__init__.py +3 -0
- remote_debug_mcp-0.2.0/src/remote_debug_mcp/__main__.py +5 -0
- remote_debug_mcp-0.2.0/src/remote_debug_mcp/config.example.yaml +30 -0
- remote_debug_mcp-0.2.0/src/remote_debug_mcp/config_loader.py +216 -0
- remote_debug_mcp-0.2.0/src/remote_debug_mcp/server.py +741 -0
- remote_debug_mcp-0.2.0/src/remote_debug_mcp/sessions.py +1173 -0
- remote_debug_mcp-0.2.0/tools_schema.json +351 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
## 项目
|
|
4
|
+
|
|
5
|
+
基于 MCP 的远程调试服务器,支持 SSH 和 Telnet 后台持久化连接。Python + pexpect 实现。
|
|
6
|
+
|
|
7
|
+
## 命令
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# 可编辑模式安装
|
|
11
|
+
pip install -e .
|
|
12
|
+
|
|
13
|
+
# 启动 MCP 服务器(stdio 传输,供 MCP 客户端调用)
|
|
14
|
+
python -m remote_debug_mcp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
暂无 lint、typecheck、测试命令配置。
|
|
18
|
+
|
|
19
|
+
## 架构
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
src/
|
|
23
|
+
├── remote_debug_mcp/ # MCP 服务端主包
|
|
24
|
+
│ ├── __init__.py # 导出 main()
|
|
25
|
+
│ ├── __main__.py # asyncio.run(main())
|
|
26
|
+
│ ├── server.py # MCP 服务端:17 个工具定义 + 分发
|
|
27
|
+
│ ├── sessions.py # SessionManager:SSH/Telnet 连接生命周期管理
|
|
28
|
+
│ ├── config_loader.py # YAML 配置文件加载/保存、SSH/Com2Tcp 配置数据类
|
|
29
|
+
│ └── config.example.yaml
|
|
30
|
+
└── remote/ # 上传远程服务器执行的 Python 脚本
|
|
31
|
+
├── pyproject.toml # 远程工具安装配置
|
|
32
|
+
└── com2telnet.py # 串口转 Telnet 服务端
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- **入口**: `remote_debug_mcp.server.main()` — 启动 MCP stdio 服务器。
|
|
36
|
+
- **SessionManager** (`sessions.py`): 通过 `get_manager()` 获取单例。持有 `SSHSession` 和 `TelnetSession` 字典,按调用方自选的 `session_id` 索引。所有 pexpect I/O 均为同步操作,通过 `loop.run_in_executor` 在线程池中执行。
|
|
37
|
+
- **SSH 会话** 使用 `pexpect.spawn('ssh', ...)` 建立原始连接(**不使用 pxssh**,以兼容 Windows cmd/PowerShell 提示符)。连接时自动检测远程平台(Linux/Windows)并统一采用「命令追加 echo 标记」策略分隔输出,不依赖 shell 提示符同步。
|
|
38
|
+
- **Telnet 会话** 使用原始 `pexpect.spawn('telnet', ...)`,支持可选的登录提示检测。
|
|
39
|
+
- **配置管理** (`config_loader.py`): 简版 YAML 解析器,无需 PyYAML。`get_config()` 首次调用自动加载 `config.yaml` 并缓存在内存。`save_config()` 是配置唯一入库入口,支持无参(保存内存配置)和带 `connections` 参数(创建/更新条目后写入文件)。SSH 连接通过 `ssh_connect(config_name=...)` 读取配置中的参数,Telnet 连接通过 `telnet_connect(config_name=...)` 从 com2tcp 配置解析全部参数(host 来自关联 SSH 配置)。
|
|
40
|
+
- **工具** 定义为 `server.py` 中模块级的 `TOOLS` 列表;`call_tool` 按名称分发到 `SessionManager` 方法。
|
|
41
|
+
- **DESIGN.md** 包含完整架构规格说明。
|
|
42
|
+
|
|
43
|
+
## SSH 命令执行策略
|
|
44
|
+
|
|
45
|
+
**统一策略**:所有目标(Linux bash / Windows cmd / PowerShell)均使用同一方式:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
child.sendline(f"{command}; echo '__MCP_CMD_{timestamp}__'")
|
|
49
|
+
child.expect("__MCP_CMD_{timestamp}__", timeout)
|
|
50
|
+
output = child.before
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
每次命令带唯一时间戳标记,不依赖 shell 提示符。彻底避免 `pexpect.pxssh` 在 Windows cmd 提示符下 "could not synchronize with original prompt" 的问题。
|
|
54
|
+
|
|
55
|
+
## 平台检测
|
|
56
|
+
|
|
57
|
+
连接后发送 `echo __MCP_PLATFORM_DETECT__ && uname -s 2>/dev/null || echo __WINDOWS__ && echo __MCP_DETECT_DONE__`,根据输出中是否含 `__WINDOWS__` 判定 linux/windows。
|
|
58
|
+
|
|
59
|
+
## SSH 文件传输 MD5 校验
|
|
60
|
+
|
|
61
|
+
上传/下载完成后自动计算本地和远程文件 MD5 并比较:
|
|
62
|
+
- Linux: `md5sum` 命令
|
|
63
|
+
- Windows: `Get-FileHash -Algorithm MD5`
|
|
64
|
+
- 一致 → 返回 `[MD5 OK: xxx]`
|
|
65
|
+
- 不一致 → 返回 `[MD5 MISMATCH! local=xxx remote=xxx]`
|
|
66
|
+
- 无法读取远程 → 返回 `(MD5 verify skipped: ...)`
|
|
67
|
+
|
|
68
|
+
## 关键约定
|
|
69
|
+
|
|
70
|
+
- 会话 ID 由调用方自行选择字符串。重复使用同一 ID 会覆盖旧会话。
|
|
71
|
+
- 所有同步 pexpect 调用必须通过 `loop.run_in_executor` 执行,避免阻塞 asyncio 事件循环。
|
|
72
|
+
- `SSHSession` 存储 `ConnectionParams`(主机、端口、用户名、密码/密钥),供自动重连使用。`powershell_available` 标记决定命令执行走交互式还是 one-shot 模式。
|
|
73
|
+
- SSH 连接统一通过 `config.yaml` 中的配置名称连接 (`ssh_connect(config_name="xxx")`),根据配置中是否有 `key_file` 自动选择密码或密钥认证。
|
|
74
|
+
- `TelnetSession` 维护一个字节缓冲区,带读指针;`telnet_listen` 消费新数据。缓冲区有可配置的最大大小(默认 64KB)。
|
|
75
|
+
- `telnet_listen` 支持 `encoding` 参数:`utf-8`(默认)、`base64` 或 `hex`,用于安全处理二进制数据。
|
|
76
|
+
- `telnet_send` 合并了原 `telnet_execute` 功能:`timeout=0` 发送后立即返回,`timeout>0` 发送后等待响应返回数据。支持 `__CTRL_C__` / `__CTRL_D__` / `__CTRL_Z__` 控制字符。普通数据自动追加 `\r`,无需调用方手动添加,确保串口终端正确执行命令。
|
|
77
|
+
- `telnet_start_monitor` 启动后台持续监听,可选输出到文件。`telnet_start_monitor` 启动后台监停并返回行数。
|
|
78
|
+
- `src/remote/` 包含上传远程服务器执行的脚本。`setup_com2tcp` 通过 SSH 上传 `com2telnet.py` + `pyproject.toml` 到 Windows PC 的 `D:\remote-debug\com2telnet\`,执行 `pip install -e` 安装依赖(pyserial),PowerShell `Start-Process -WindowStyle Hidden` 后台启动 `com2telnet --serial COMx:PORT:BAUD`,之后调用方可 `telnet_connect(config_name="com2tcp_COM4_5200")` 访问串口数据(host 自动从关联 SSH 配置解析)。
|
|
79
|
+
- `config.yaml` 固定为配置文件路径(自动搜索多个位置)。`list_connections` 查看已加载配置,`save_config` 将内存配置写回文件。
|
|
80
|
+
- `telnet_connect` 仅需 `session_id` + `config_name`,所有连接参数(host、port、username、password、timeout、buffer_max_size、max_retries)均从 com2tcp 配置解析,无需 LLM 传递。
|
|
81
|
+
- `save_config` 是配置唯一入库入口:无参保存内存配置,带 `connections` 参数创建/更新条目后写入文件。无 `config.yaml` 时 LLM 应询问用户参数后调用此工具。
|
|
82
|
+
- `Com2TcpConfig` 扩展字段:`username`, `password`, `connect_timeout`(15s), `buffer_max_size`(64KB), `max_retries`(3),均有默认值,向后兼容。
|
|
83
|
+
- `setup_com2tcp` 完成后会提示 LLM 调用 `save_config` 持久化 com2tcp 配置。
|
|
84
|
+
|
|
85
|
+
## 自动重连
|
|
86
|
+
|
|
87
|
+
SSH 和 Telnet 会话均支持连接断开后自动重连。可通过 `max_retries`(默认 3)和指数退避进行配置。重连参数存储在各会话的 `ConnectionParams` 中。
|
|
88
|
+
|
|
89
|
+
## 注意事项
|
|
90
|
+
|
|
91
|
+
- **不使用 pexpect.pxssh**。Windows cmd/PowerShell 的提示符格式与 Unix shell 不同,pxssh 的 `sync_original_prompt()` 在 Windows 上必然失败。
|
|
92
|
+
- Telnet `telnet_listen` 会在线程池中阻塞完整的 `duration` 秒 — 保持合理的持续时间(≤ 60s)。
|
|
93
|
+
- SCP 传输(`ssh_upload` / `ssh_download`)对含特殊字符或空格的 Windows 路径可能失败,会自动降级到 SFTP。
|
|
94
|
+
- 在 Windows SSH 目标上,过长的 PowerShell 命令行可能会被截断。
|
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
# Remote Debug MCP — 设计文档
|
|
2
|
+
|
|
3
|
+
## 1. 概述
|
|
4
|
+
|
|
5
|
+
基于 **MCP (Model Context Protocol)** 构建的远程调试服务器,通过 **SSH** 和 **Telnet** 提供后台持久化连接。使用 **Python + pexpect** 实现,通过 stdio 传输与 MCP 客户端(如 Claude Desktop、OpenCode 等)通信。
|
|
6
|
+
|
|
7
|
+
### 核心能力
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌─────────────────────────────────────────────────┐
|
|
11
|
+
│ MCP Client │
|
|
12
|
+
│ (Claude Desktop / OpenCode) │
|
|
13
|
+
└─────────────────┬───────────────────────────────┘
|
|
14
|
+
│ stdio (JSON-RPC)
|
|
15
|
+
┌─────────────────▼───────────────────────────────┐
|
|
16
|
+
│ remote-debug-mcp │
|
|
17
|
+
│ ┌───────────────────────────────────────────┐ │
|
|
18
|
+
│ │ server.py │ │
|
|
19
|
+
│ │ • 17 MCP 工具定义 & 注册 │ │
|
|
20
|
+
│ │ • call_tool 分发 → SessionManager │ │
|
|
21
|
+
│ └─────────────────┬─────────────────────────┘ │
|
|
22
|
+
│ │ │
|
|
23
|
+
│ ┌─────────────────▼─────────────────────────┐ │
|
|
24
|
+
│ │ sessions.py │ │
|
|
25
|
+
│ │ ┌──────────┐ ┌──────────┐ ┌─────────┐ │ │
|
|
26
|
+
│ │ │SSHSession│ │ Telnet │ │ com2tcp │ │ │
|
|
27
|
+
│ │ │ (spawn) │ │ (spawn) │ │ workflow │ │ │
|
|
28
|
+
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
|
|
29
|
+
│ │ │ │ │ │ │
|
|
30
|
+
│ │ loop.run_in_executor (线程池) │ │
|
|
31
|
+
│ └───────┼──────────────┼──────────────┼────────┘ │
|
|
32
|
+
│ │ │ │ │
|
|
33
|
+
└──────────┼──────────────┼──────────────┼────────────┘
|
|
34
|
+
│ │ │
|
|
35
|
+
┌──────▼──────┐ ┌─────▼──────┐ ┌────▼──────────┐
|
|
36
|
+
│ Remote Linux │ │ Remote │ │ Remote Windows │
|
|
37
|
+
│ Server │ │ Telnet │ │ (COM port) │
|
|
38
|
+
└─────────────┘ └────────────┘ └───────────────┘
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 2. 连接架构
|
|
44
|
+
|
|
45
|
+
### 2.1 会话生命周期
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
┌──────────────┐
|
|
49
|
+
connect──▶│ connecting │
|
|
50
|
+
└──────┬───────┘
|
|
51
|
+
│ auth OK
|
|
52
|
+
┌──────▼───────┐
|
|
53
|
+
│ connected │◄──── reconnect ────┐
|
|
54
|
+
└──┬───────┬───┘ │
|
|
55
|
+
execute cmd │ │ connection lost │
|
|
56
|
+
(复用会话) │ ▼ │
|
|
57
|
+
┌──▼──────────────┐ retry OK │
|
|
58
|
+
│ disconnected │─────────────────┘
|
|
59
|
+
└─────────────────┘
|
|
60
|
+
│
|
|
61
|
+
disconnect (主动)
|
|
62
|
+
▼
|
|
63
|
+
┌─────────────────┐
|
|
64
|
+
│ closed/removed │
|
|
65
|
+
└─────────────────┘
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- **Session ID**: 调用方自选字符串,重复使用会覆盖旧会话
|
|
69
|
+
- **会话状态**: `connecting` → `connected` → `disconnected`
|
|
70
|
+
- **自动重连**: 连接断开后自动重试(最多 N 次,指数退避),重连成功后恢复 `connected` 状态
|
|
71
|
+
|
|
72
|
+
### 2.2 连接参数保存
|
|
73
|
+
|
|
74
|
+
每次 connect 时保存完整连接参数,供自动重连使用:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
@dataclass
|
|
78
|
+
class ConnectionParams:
|
|
79
|
+
host: str
|
|
80
|
+
port: int
|
|
81
|
+
username: str = "" # SSH/Telnet 用户名
|
|
82
|
+
password: str = "" # SSH 密码(明文内存)
|
|
83
|
+
key_file: str = "" # SSH 密钥路径
|
|
84
|
+
connect_timeout: int = 30 # SSH 默认 30s,Telnet 调用方按需覆盖
|
|
85
|
+
max_retries: int = 3
|
|
86
|
+
retry_backoff: float = 2.0 # 退避倍数
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 3. SSH 子系统设计
|
|
92
|
+
|
|
93
|
+
### 3.1 平台适配与命令执行
|
|
94
|
+
|
|
95
|
+
**不使用 pexpect.pxssh**,改为原始 `pexpect.spawn('ssh', ...)`。pxssh 的 `sync_original_prompt()` 和 `login()` 依赖 Unix shell 提示符格式,在 Windows cmd(提示符如 `C:\Users\xxx>$P$G`)或 PowerShell 下必然失败,报错 "could not synchronize with original prompt"。
|
|
96
|
+
|
|
97
|
+
**命令执行策略**:每次命令追加唯一 echo 标记,收集原始字节,按平台分编码解码输出。
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
Linux / Windows 统一方式:
|
|
101
|
+
marker = f"__MCP_CMD_{timestamp}__"
|
|
102
|
+
child.send(f"{command}; echo {marker}".encode(platform_encoding) + b"\n")
|
|
103
|
+
收集后按编码解码: output = raw.decode(platform_encoding)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
目标系统分两种,连接后自动检测:
|
|
107
|
+
|
|
108
|
+
| 平台 | Shell | 检测方式 | 命令编码 | 输出解码 |
|
|
109
|
+
|------|-------|---------|---------|---------|
|
|
110
|
+
| **Linux** | bash/sh | `echo __MCP_PLATFORM_DETECT__ && uname -s ... ` | UTF-8 | UTF-8 |
|
|
111
|
+
| **Windows (`-T`)** | CMD → PowerShell | 同上,输出中含 `__WINDOWS__` 则判定 | GBK | GBK |
|
|
112
|
+
| **Windows (`-t`)** | PowerShell (PTY) | `-T` 失败回退 `-t`,`chcp 65001` | UTF-8 | UTF-8 |
|
|
113
|
+
|
|
114
|
+
连接后尝试从 CMD 切换到 PowerShell(`powershell` 命令),成功则工作目录 `D:\remote_debug`。
|
|
115
|
+
|
|
116
|
+
### 3.1.1 Windows 双模命令执行
|
|
117
|
+
|
|
118
|
+
Windows 目标可能因 SSH 服务端配置差异,导致交互式 shell 无法正常回传 stdout。系统通过健康检测自动选择执行模式:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
连接 → 平台检测(Windows) → 尝试 PS 切换 → _verify_shell 健康检测
|
|
122
|
+
├─ 通过 → powershell_available=True → 交互式 send() 执行
|
|
123
|
+
└─ 失败 → powershell_available=False → 重建 CMD 连接
|
|
124
|
+
└─ 后续命令走 _ssh_execute_one_shot
|
|
125
|
+
每次新起 ssh -T host "cmd & echo MARKER"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**交互式模式**(`powershell_available=True`):
|
|
129
|
+
- `child.send(f"{cmd}; echo MARKER\n")` — PowerShell 用 `;` 分隔符
|
|
130
|
+
- 循环 `read_nonblocking` 等待 MARKER
|
|
131
|
+
- win-pc 等标准 Windows 桌面使用此模式
|
|
132
|
+
|
|
133
|
+
**One-shot 模式**(`powershell_available=False`):
|
|
134
|
+
- 每次命令 `pexpect.spawn('ssh', ['-T', ..., 'host', 'command & echo MARKER'])`
|
|
135
|
+
- 直接捕获 stdout,不受 shell 交互状态影响
|
|
136
|
+
- server-12 等 SSH 服务端配置受限的机器使用此模式
|
|
137
|
+
|
|
138
|
+
### 3.2 中文编码处理
|
|
139
|
+
|
|
140
|
+
pexpect 使用 `encoding=None`(原始字节模块),所有编解码由应用层控制:
|
|
141
|
+
|
|
142
|
+
**Windows `-T` 无 PTY 模式**(如 win-pc):
|
|
143
|
+
```
|
|
144
|
+
┌─────────┐ GBK 编码命令 ┌──────────────┐
|
|
145
|
+
│ MCP │ ──────────────────▶ │ PowerShell │
|
|
146
|
+
│ Server │ │ (GBK) │
|
|
147
|
+
│ │ ◀── 原始字节 ──── │ │
|
|
148
|
+
│ │ → GBK 解码输出 └──────────────┘
|
|
149
|
+
└─────────┘
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Windows `-t` PTY 模式**(如 server-12,`-T` 失败回退):
|
|
153
|
+
```
|
|
154
|
+
┌─────────┐ chcp 65001 + UTF-8 ┌──────────────┐
|
|
155
|
+
│ MCP │ ──────────────────▶ │ PowerShell │
|
|
156
|
+
│ Server │ │ (UTF-8 PTY) │
|
|
157
|
+
│ │ ◀── 原始字节 ──── │ │
|
|
158
|
+
│ │ → UTF-8 解码输出 └──────────────┘
|
|
159
|
+
└─────────┘
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Linux 流程**:
|
|
163
|
+
```
|
|
164
|
+
┌─────────┐ UTF-8 编码命令 ┌──────────────┐
|
|
165
|
+
│ MCP │ ──────────────────▶ │ bash/sh │
|
|
166
|
+
│ Server │ │ (UTF-8) │
|
|
167
|
+
│ │ ◀── 原始字节 ──── │ │
|
|
168
|
+
│ │ → UTF-8 解码输出 └──────────────┘
|
|
169
|
+
└─────────┘
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
关键点:
|
|
173
|
+
- pexpect `encoding=None` 避免内部编解码损失字节
|
|
174
|
+
- Windows `-T` 模式命令含中文参数时用 GBK 编码
|
|
175
|
+
- Windows `-t` 模式 PTY 建立后执行 `chcp 65001` 切换 UTF-8,编解码统一 UTF-8
|
|
176
|
+
- 标记 `__MCP_CMD_<ts>__` 纯 ASCII,编码无关
|
|
177
|
+
|
|
178
|
+
### 3.3 文件传输
|
|
179
|
+
|
|
180
|
+
| 方式 | 适用目标 | 原理 |
|
|
181
|
+
|------|---------|------|
|
|
182
|
+
| **SCP** | Linux + Windows | `pexpect.spawn('scp ...')`,处理密码提示 |
|
|
183
|
+
| **SFTP** | Linux + Windows | `pexpect.spawn('sftp ...')`,交互式批处理 |
|
|
184
|
+
|
|
185
|
+
**优先级**:SCP 优先 → SFTP 兜底。SCP 不支持含空格路径时自动降级到 SFTP。
|
|
186
|
+
|
|
187
|
+
**路径规范化**:Windows 目标自动将远程路径转为 `/D:/path/to/file` 格式(正斜杠 + 前缀斜杠),兼容 SCP 和 SFTP。
|
|
188
|
+
|
|
189
|
+
**SFTP 自动创建目录**:上传时若父目录不存在,SFTP 自动逐级 `mkdir` 创建(UTF-8 编码,兼容中文目录名)。
|
|
190
|
+
|
|
191
|
+
**MD5 校验**:上传/下载完成后自动计算本地与远程文件 MD5 并比较,结果以 `[MD5 OK: xxx]` / `[MD5 MISMATCH!]` / `[(MD5 verify skipped)]` 格式追加到传输结果尾部。
|
|
192
|
+
|
|
193
|
+
### 3.4 自动重连
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
执行命令
|
|
197
|
+
│
|
|
198
|
+
├─ 成功 → 返回输出
|
|
199
|
+
│
|
|
200
|
+
└─ 失败 (EOF / connection reset)
|
|
201
|
+
│
|
|
202
|
+
├─ retry_count < max_retries
|
|
203
|
+
│ sleep(backoff * 2^retry_count)
|
|
204
|
+
│ 重新 _ssh_spawn() 连接
|
|
205
|
+
│ ├─ 成功 → 重试原命令
|
|
206
|
+
│ └─ 失败 → retry_count++
|
|
207
|
+
│
|
|
208
|
+
└─ retry_count >= max_retries
|
|
209
|
+
→ 标记 disconnected,返回错误
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 4. Telnet 子系统设计
|
|
215
|
+
|
|
216
|
+
### 4.1 连接流程
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
spawn('telnet', [host, port])
|
|
220
|
+
│
|
|
221
|
+
├─ "Escape character is" → 连接成功(无登录)
|
|
222
|
+
├─ "login:" / "Login:" / "Username:" / "User:" / "username:" → 需要登录
|
|
223
|
+
│ sendline(username)
|
|
224
|
+
│ expect "Password:"
|
|
225
|
+
│ sendline(password)
|
|
226
|
+
│ expect 提示符
|
|
227
|
+
└─ TIMEOUT / EOF → 连接失败
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### 4.2 缓冲区管理(消费模式)
|
|
231
|
+
|
|
232
|
+
每个 Telnet 会话维护一个内部循环缓冲区:
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
@dataclass
|
|
236
|
+
class TelnetSession:
|
|
237
|
+
...
|
|
238
|
+
buffer: bytes = b"" # 循环缓冲区
|
|
239
|
+
buffer_max_size: int = 65536 # 64KB 默认,可配置
|
|
240
|
+
read_cursor: int = 0 # 读指针(已消费位置)
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**数据流**:
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
远程串口 ──continuous──▶ com2tcp ──telnet──▶ TelnetSession.buffer
|
|
247
|
+
│
|
|
248
|
+
telnet_listen(duration) │
|
|
249
|
+
◄──────────────────────────┘
|
|
250
|
+
返回 buffer[read_cursor:] (新数据)
|
|
251
|
+
read_cursor 移到末尾
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
| 工具 | 行为 | 移动 cursor |
|
|
255
|
+
|------|------|------------|
|
|
256
|
+
| `telnet_listen` | 监听 duration 秒,返回期间收到的新数据 | 是 |
|
|
257
|
+
|
|
258
|
+
`telnet_read` / `telnet_read_all` 已删除,合并为 `telnet_listen` 覆盖。`telnet_send` 使用 echo-marker 策略(与 SSH 统一):发送 `command; echo __MCP_{ts}__`,两次 `expect(marker)` 分别消费命令回显和捕获输出,命令执行完即刻返回,不再盲等 timeout 秒。普通数据自动追加 `\r`。
|
|
259
|
+
|
|
260
|
+
### 4.3 二进制数据处理
|
|
261
|
+
|
|
262
|
+
串口数据可能包含非 UTF-8 字节。Telnet 缓冲区存储 **原始 bytes**,返回时提供两种编码选项:
|
|
263
|
+
|
|
264
|
+
- `encoding="utf-8"` (默认): 用 `errors="replace"` 解码为字符串
|
|
265
|
+
- `encoding="base64"`: 返回 base64 编码的原始字节
|
|
266
|
+
- `encoding="hex"`: 返回十六进制编码
|
|
267
|
+
|
|
268
|
+
### 4.4 后台持续监听
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
telnet_start_monitor(output_file?)
|
|
272
|
+
│
|
|
273
|
+
├─ 启动 daemon 后台线程
|
|
274
|
+
│ while monitor_active:
|
|
275
|
+
│ data = child.read_nonblocking(4096)
|
|
276
|
+
│ lines.append(data.split(b"\n"))
|
|
277
|
+
│ if output_file: f.write(data)
|
|
278
|
+
│
|
|
279
|
+
├─ 行列缓存: deque(maxlen=900000) FIFO 淘汰
|
|
280
|
+
│
|
|
281
|
+
├─ telnet_send / read / listen 可并发使用
|
|
282
|
+
│ io_lock 保护 PTY 读写
|
|
283
|
+
│
|
|
284
|
+
└─ telnet_stop_monitor → 停止线程,返回行数
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
monitor 运行时 `telnet_listen` 从 deque 取数据,非激活时沿用旧 buffer 路径。`telnet_send` 支持控制字符 `__CTRL_C__`(0x03)、`__CTRL_Z__`(0x1a)、`__CTRL_D__`(0x04),普通数据自动追加 `\r` 确保串口终端正确执行命令。
|
|
288
|
+
|
|
289
|
+
### 4.5 自动重连
|
|
290
|
+
|
|
291
|
+
Telnet 同样支持自动重连。重连时重新执行完整的连接 + 登录流程。
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## 5. com2tcp 工作流
|
|
296
|
+
|
|
297
|
+
### 5.1 场景与配置链路
|
|
298
|
+
|
|
299
|
+
```
|
|
300
|
+
LLM 与 MCP 交互流程:
|
|
301
|
+
无 config.yaml → LLM 询问用户参数 → save_config(connections=...)
|
|
302
|
+
setup_com2tcp 完成 → save_config 持久化 com2tcp 配置
|
|
303
|
+
telnet_connect(config_name="com2tcp_COM4_5200") → 自动解析 host/port
|
|
304
|
+
|
|
305
|
+
数据流:
|
|
306
|
+
┌──────────────┐ SSH (PowerShell) ┌─────────────────────────┐
|
|
307
|
+
│ MCP Server │ ───────────────────────▶ │ Windows PC (目标机) │
|
|
308
|
+
│ │ │ │
|
|
309
|
+
│ │ 1. upload com2telnet.py │ D:\remote_debug\ │
|
|
310
|
+
│ │ + pyproject.toml │ \com2telnet\ │
|
|
311
|
+
│ │ 2. pip install dep │ │
|
|
312
|
+
│ │ 3. 后台启动 com2telnet │ │
|
|
313
|
+
│ │ │ │
|
|
314
|
+
│ │ Telnet │ COM4 ◄── 串口设备 │
|
|
315
|
+
│ │ ◄─────────────────────── │ :5200 (telnet) │
|
|
316
|
+
└──────────────┘ └─────────────────────────┘
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### 5.2 执行步骤
|
|
320
|
+
|
|
321
|
+
```
|
|
322
|
+
1. 前置条件: SSH 已连接到 Windows PC (session_id),
|
|
323
|
+
config.yaml 中已有 SSH 配置(名称如 "windows-pc")。
|
|
324
|
+
|
|
325
|
+
2. 上传 com2telnet.py + pyproject.toml
|
|
326
|
+
setup_com2tcp 自动通过 SSH 上传脚本到 D:\remote-debug\com2telnet\
|
|
327
|
+
pip install -e 安装依赖(pyserial)
|
|
328
|
+
|
|
329
|
+
3. 终止旧进程 + 后台启动 + 验证
|
|
330
|
+
|
|
331
|
+
4. 提示 LLM 调用 save_config 持久化 com2tcp 配置:
|
|
332
|
+
save_config(connections=[{
|
|
333
|
+
name: "com2tcp_COM4_5200", type: "com2tcp",
|
|
334
|
+
ssh: "windows-pc", com_port: "COM4",
|
|
335
|
+
telnet_port: 5200, baud: 115200
|
|
336
|
+
}])
|
|
337
|
+
|
|
338
|
+
5. 后续连接无需传递 host/port,只需:
|
|
339
|
+
telnet_connect(session_id="serial", config_name="com2tcp_COM4_5200")
|
|
340
|
+
→ 系统自动解析 host(从 SSH 配置)+ port(从 com2tcp 配置)
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### 5.3 参数
|
|
344
|
+
|
|
345
|
+
| 参数 | 默认值 | 说明 |
|
|
346
|
+
|------|--------|------|
|
|
347
|
+
| `baud` | 115200 | 串口波特率 |
|
|
348
|
+
| `--host` | 0.0.0.0 | Telnet 服务绑定地址 |
|
|
349
|
+
| `--serial` PORT:PORT[:BAUD] | — | 串口到 Telnet 映射 |
|
|
350
|
+
|
|
351
|
+
### 5.4 Com2TcpConfig 数据模型
|
|
352
|
+
|
|
353
|
+
```python
|
|
354
|
+
@dataclass
|
|
355
|
+
class Com2TcpConfig:
|
|
356
|
+
name: str # 配置名(e.g. "com2tcp_COM4_5200")
|
|
357
|
+
ssh: str # 关联的 SSH 配置名(用于解析 host)
|
|
358
|
+
com_port: str # COM 端口名
|
|
359
|
+
telnet_port: int = 5200 # Telnet 端口
|
|
360
|
+
baud: int = 115200 # 波特率
|
|
361
|
+
username: str = "" # Telnet 登录用户名
|
|
362
|
+
password: str = "" # Telnet 登录密码
|
|
363
|
+
connect_timeout: int = 15 # 连接超时(秒)
|
|
364
|
+
buffer_max_size: int = 65536 # 缓冲区大小(字节)
|
|
365
|
+
max_retries: int = 3 # 自动重连次数
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
所有扩展字段均有默认值,向后兼容旧配置文件。
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## 6. 线程模型
|
|
373
|
+
|
|
374
|
+
```
|
|
375
|
+
asyncio event loop (主线程)
|
|
376
|
+
│
|
|
377
|
+
├─ stdio_server() ── MCP 协议消息循环
|
|
378
|
+
│
|
|
379
|
+
└─ call_tool(name, args) [async]
|
|
380
|
+
│
|
|
381
|
+
└─ loop.run_in_executor(None, sync_func, ...)
|
|
382
|
+
│
|
|
383
|
+
└─ ThreadPoolExecutor (默认线程池)
|
|
384
|
+
│
|
|
385
|
+
├─ mgr.ssh_connect(...) # 阻塞式 pexpect
|
|
386
|
+
├─ mgr.ssh_execute(...)
|
|
387
|
+
├─ mgr.telnet_connect(...)
|
|
388
|
+
└─ mgr.telnet_listen(...) # 会阻塞 duration 秒
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**关键约束**:
|
|
392
|
+
- 长时间阻塞的 `pexpect` 调用(`ssh_connect`、`ssh_execute`、`telnet_connect`、`telnet_listen`)必须在 `run_in_executor` 中
|
|
393
|
+
- 轻量操作(`ssh_disconnect`、`telnet_disconnect`、`telnet_send(timeout=0)`)因其执行时间极短(毫秒级),直接在事件循环线程中执行,避免不必要的线程调度开销
|
|
394
|
+
- `telnet_listen` 会长时间占用线程,duration 不宜过大(建议 ≤ 60s)
|
|
395
|
+
- `SessionManager._lock` 保护 session dict 的并发访问
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## 7. 错误处理
|
|
400
|
+
|
|
401
|
+
### 7.1 错误分类
|
|
402
|
+
|
|
403
|
+
| 类型 | 示例 | 处理 |
|
|
404
|
+
|------|------|------|
|
|
405
|
+
| 连接错误 | host unreachable, auth failed | 返回错误信息,不重连 |
|
|
406
|
+
| 传输错误 | connection reset, EOF | 触发自动重连逻辑 |
|
|
407
|
+
| 超时 | 命令执行超时 | 返回部分输出 + `[TIMEOUT]` 标记 |
|
|
408
|
+
| 协议错误 | 未知工具名 | 返回 `Unknown tool: {name}` |
|
|
409
|
+
|
|
410
|
+
### 7.2 返回格式
|
|
411
|
+
|
|
412
|
+
成功:
|
|
413
|
+
```
|
|
414
|
+
SSH connected: user@10.0.0.1:22 [session=myssh]
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
失败:
|
|
418
|
+
```
|
|
419
|
+
SSH connection failed [myssh]: Authentication failed
|
|
420
|
+
[TIMEOUT] Command exceeded 30s: long_running_task
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## 8. 安全考虑
|
|
426
|
+
|
|
427
|
+
- 密码在内存中明文存储(`ConnectionParams.password`),进程终止后清除
|
|
428
|
+
- SSH `StrictHostKeyChecking=no` 跳过主机密钥验证(内网调试场景可接受)
|
|
429
|
+
- 不在日志中输出密码
|
|
430
|
+
- 建议生产环境使用密钥认证(在 config.yaml 中配置 `key_file` 字段,`ssh_connect` 自动识别)
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## 9. MCP 工具清单
|
|
435
|
+
|
|
436
|
+
### SSH (6 个)
|
|
437
|
+
|
|
438
|
+
| 工具 | 说明 | 关键参数 |
|
|
439
|
+
|------|------|---------|
|
|
440
|
+
| `ssh_connect` | 通过 config_name 从 config.yaml 读取参数连接(密码/密钥自适应),自动检测平台并选最优执行模式 | session_id, config_name, max_retries? |
|
|
441
|
+
| `ssh_execute` | 执行命令(自动适配 bash/PowerShell,中文编码正确) | session_id, command, timeout |
|
|
442
|
+
| `ssh_upload` | SCP 上传(自动降级 SFTP,空格兼容,中文目录自动 mkdir,MD5 校验) | session_id, local_path, remote_path |
|
|
443
|
+
| `ssh_download` | SCP 下载(自动降级 SFTP,空格兼容,MD5 校验) | session_id, remote_path, local_path |
|
|
444
|
+
| `ssh_disconnect` | 关闭会话 | session_id |
|
|
445
|
+
| `ssh_list` | 列出会话 | — |
|
|
446
|
+
|
|
447
|
+
### Telnet (5 个) + 监控 (2 个)
|
|
448
|
+
|
|
449
|
+
| 工具 | 说明 | 关键参数 |
|
|
450
|
+
|------|------|---------|
|
|
451
|
+
| `telnet_connect` | 通过 com2tcp config_name 连接(host/port/login/buffer/retries 全部从配置解析,无需 LLM 传参) | session_id, config_name |
|
|
452
|
+
| `telnet_send` | 发送数据(timeout=0 发后即返,timeout>0 等响应;支持 `__CTRL_C__`/`__CTRL_D__`/`__CTRL_Z__`;普通数据自动追加 `\r`) | session_id, data, timeout |
|
|
453
|
+
| `telnet_listen` | 监听新数据(支持 utf-8/base64/hex 编码) | session_id, duration, encoding |
|
|
454
|
+
| `telnet_start_monitor` | 启动后台持续监听,可选文件输出 | session_id, output_file? |
|
|
455
|
+
| `telnet_stop_monitor` | 停止后台监听 | session_id |
|
|
456
|
+
| `telnet_disconnect` | 关闭会话 | session_id |
|
|
457
|
+
| `telnet_list` | 列出会话 | — |
|
|
458
|
+
|
|
459
|
+
### 工作流 (1 个)
|
|
460
|
+
|
|
461
|
+
| 工具 | 说明 |
|
|
462
|
+
|------|------|
|
|
463
|
+
| `setup_com2tcp` | SSH 上传 + 后台启动 com2tcp,返回 telnet 连接信息 |
|
|
464
|
+
|
|
465
|
+
### 配置 (2 个)
|
|
466
|
+
|
|
467
|
+
| 工具 | 说明 | 关键参数 |
|
|
468
|
+
|------|------|---------|
|
|
469
|
+
| `list_connections` | 列出已加载配置中的所有 SSH 和 com2tcp 连接 | — |
|
|
470
|
+
| `save_config` | **配置唯一入库入口**。无参:保存内存配置。带 `connections` 参数:合并条目后写入文件,支持 type=ssh `{name,host,port,username,password,key_file?}` 和 type=com2tcp `{name,ssh,com_port,telnet_port,baud,connect_timeout?,buffer_max_size?,max_retries?,username?,password?}`。无配置文件时自动创建 | connections? |
|
|
471
|
+
|
|
472
|
+
### 通用 (1 个)
|
|
473
|
+
|
|
474
|
+
| 工具 | 说明 |
|
|
475
|
+
|------|------|
|
|
476
|
+
| `list_sessions` | 列出所有 SSH 和 Telnet 会话 |
|
|
477
|
+
|
|
478
|
+
**总计: 17 个工具**
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## 10. 测试策略
|
|
483
|
+
|
|
484
|
+
| 层级 | 内容 | 方式 |
|
|
485
|
+
|------|------|------|
|
|
486
|
+
| 单元测试 | SessionManager 状态管理、工具注册 | 本地可运行 |
|
|
487
|
+
| 集成测试 | SSH/Telnet 连接真实目标 | 需要测试环境 |
|
|
488
|
+
| 模拟测试 | Mock pexpect 验证流程 | 自动化 CI |
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## 11. 后续扩展
|
|
493
|
+
|
|
494
|
+
- [ ] Telnet 数据流式推送(通过 MCP 通知)
|
|
495
|
+
- [ ] SSH 端口转发(本地/远程)
|
|
496
|
+
- [ ] 多 COM 端口同时桥接
|
|
497
|
+
- [ ] 会话心跳保活
|
|
498
|
+
- [ ] 连接日志持久化
|
|
499
|
+
- [x] ~~配置文件支持(YAML 预设连接参数)~~
|
|
500
|
+
- [x] ~~Base64 传输删除(仅保留 SCP + SFTP)~~
|
|
501
|
+
- [x] ~~中文编码处理(GBK/UTF-8 自适应)~~
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
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.
|