docs: redesign Phase 9 Serial MCP as GUI-owned port + named pipe bridge

GUI holds the COM port exclusively; ETTerms.SerialMcp becomes a thin client that bridges over a local named pipe (SerialBridgeServer). AI TX/RX flows through the GUI channel so it is visible live in the terminal view. Tools change from open/close to attach/detach.
This commit is contained in:
2026-06-04 15:05:36 +08:00
parent 37460e22ca
commit f4207f8586
2 changed files with 60 additions and 37 deletions
+56 -33
View File
@@ -32,7 +32,7 @@ ETTerms 是一個給工程師 / 韌體 / 硬體驗證人員用的**單一視窗
| 祕密儲存 | **Windows Credential Manager**DPAPI / CredMan | 連線密碼、SSH key passphrase,不落地明碼 | | 祕密儲存 | **Windows Credential Manager**DPAPI / CredMan | 連線密碼、SSH key passphrase,不落地明碼 |
| PDU 控制(選用) | **SnmpSharpNet** | 沿用 MyTeraTerm PDU 控制(`pductrl` / `pduconnect` | | PDU 控制(選用) | **SnmpSharpNet** | 沿用 MyTeraTerm PDU 控制(`pductrl` / `pduconnect` |
| 日誌 | 自製 **AppLogger**(從 MyTeraTerm 移植) | 檔案 + Debug 雙輸出 | | 日誌 | 自製 **AppLogger**(從 MyTeraTerm 移植) | 檔案 + Debug 雙輸出 |
| AI / MCP 整合(選用) | **stdio MCP server**(官方 C# SDK `ModelContextProtocol` | 獨立行程把 serial port 暴露成 AI 可呼叫工具(Kiro CLI / Claude CLI),見 [AI / MCP Integration](#ai--mcp-integrationserial-mcp-server) | | AI / MCP 整合(選用) | **stdio MCP server**(官方 C# SDK `ModelContextProtocol` | 獨立行程,但不自己開 port——經本機 named pipe 橋接到 GUI 持有的 serial session,把收發暴露成 AI 可呼叫工具(Kiro CLI / Claude CLI),見 [AI / MCP Integration](#ai--mcp-integrationserial-mcp-server) |
| 打包 | `dotnet publish` + (選用)Inno Setup / MSIX | 單機安裝,current-user | | 打包 | `dotnet publish` + (選用)Inno Setup / MSIX | 單機安裝,current-user |
> **與舊版 MyTeraTerm 的關鍵差異:** 舊版是把真正的 `ttermpro.exe`TeraTerm)嵌進 Panel,靠 **com0com 虛擬 COM 對**攔截 serial 來跑腳本。ETTerms 改走**全原生**SSH.NET 做 SSH、`System.IO.Ports` 做 serial、自繪 VT100 控制項做終端機畫面,**不再依賴外部 TeraTerm exe,也不再需要 com0com**。腳本引擎從「驅動 com0com bridge」改成「驅動原生 `ISessionChannel`」。 > **與舊版 MyTeraTerm 的關鍵差異:** 舊版是把真正的 `ttermpro.exe`TeraTerm)嵌進 Panel,靠 **com0com 虛擬 COM 對**攔截 serial 來跑腳本。ETTerms 改走**全原生**SSH.NET 做 SSH、`System.IO.Ports` 做 serial、自繪 VT100 控制項做終端機畫面,**不再依賴外部 TeraTerm exe,也不再需要 com0com**。腳本引擎從「驅動 com0com bridge」改成「驅動原生 `ISessionChannel`」。
@@ -141,7 +141,8 @@ ETTerms/
│ ├── SerialChannel.cs # System.IO.Ports 實作 │ ├── SerialChannel.cs # System.IO.Ports 實作
│ ├── ShellChannel.cs # Windows ConPTY 本機 Shell │ ├── ShellChannel.cs # Windows ConPTY 本機 Shell
│ ├── SessionPage.cs # 一個分頁 = TerminalView + Channel + 狀態 │ ├── SessionPage.cs # 一個分頁 = TerminalView + Channel + 狀態
── SessionManager.cs # 開 / 關 / 列舉所有 active session ── SessionManager.cs # 開 / 關 / 列舉所有 active session
│ └── SerialBridgeServer.cs# 🔜 本機 named pipe server:把 serial session 的讀寫橋接給 MCPPhase 9
├── Connections/ # ── 連線資料 ── ├── Connections/ # ── 連線資料 ──
│ ├── Connection.cs # 連線 metadata 模型 │ ├── Connection.cs # 連線 metadata 模型
@@ -160,9 +161,10 @@ ETTerms/
├── AppSettings.cs # 使用者偏好 (JSON, %LocalAppData%\ETTerms\settings.json) ├── AppSettings.cs # 使用者偏好 (JSON, %LocalAppData%\ETTerms\settings.json)
└── NativeTheme.cs # 深色標題列 (DWM) └── NativeTheme.cs # 深色標題列 (DWM)
└── ETTerms.SerialMcp/ # 🔜 Serial MCP serverstdio,給 AI agent 直接操作 serial └── ETTerms.SerialMcp/ # 🔜 Serial MCP serverstdio)——不自己開 port,經 named pipe 橋接 GUI
├── Program.cs # stdio MCP host 進入點 ├── Program.cs # stdio MCP host 進入點
├── SerialTools.cs # serial_list / open / write / read / close 工具 ├── SerialBridgeClient.cs # 連 GUI 的 named pipe,轉發 write / 接收 RX
├── SerialTools.cs # serial_list / attach / write / read / detach 工具(轉發到 pipe
└── ETTerms.SerialMcp.csproj# net8.0 console + ModelContextProtocol SDK └── ETTerms.SerialMcp.csproj# net8.0 console + ModelContextProtocol SDK
``` ```
@@ -354,40 +356,59 @@ ScriptRunner.RunAsync(scriptText, activeChannel)
## AI / MCP IntegrationSerial MCP Server ## AI / MCP IntegrationSerial MCP Server
> 讓 **Kiro CLI / Claude CLI** 等 AI agent 直接對 serial port 下指令、讀輸出。ETTerms 額外提供一支獨立的 **stdio MCP server**`src/ETTerms.SerialMcp/`),把 serial port 包成 AI 可呼叫的工具。它與 WinForms 主程式**各自獨立行程**:由 MCP clientKiro CLI / Claude CLI)啟動並維持整個 session 存活,因此能**持續持有 COM port**——連線狀態可跨多次工具呼叫保留,也能接收裝置主動推送的非同步輸出 > 讓 **Kiro CLI / Claude CLI** 等 AI agent 收發 serial**且使用者能在 ETTerms GUI 即時看到 AI 的每筆收發**
>
> **關鍵設計:COM port 由 GUI 唯一持有,MCP server 不自己開 port。** 一個 COM port 同一時間只能被一個行程開啟;若讓 MCP server 自己開,GUI 就無法同時開、使用者也看不到。因此改成 **GUI 當 port 的唯一擁有者**,在 GUI 內跑一支本機 **named pipe server**`SerialBridgeServer`);獨立的 `ETTerms.SerialMcp`(由 Kiro/Claude CLI 啟動)退化成**瘦客戶端**,所有 `write` / `read` 都經 pipe 轉發給 GUI,由 GUI 代為讀寫實體 port。
**為何要獨立常駐行程?** CLI agent 的每條 shell 指令都是一個新行程,`open→write→read` 無法跨呼叫保留狀態(port 一關就斷)。常駐的 MCP server 才能維持一條連線、累積 RX。 ### 全閉迴路(推薦用法)
整個迴路可全部跑在 ETTerms GUI 內,使用者一邊看、AI 一邊操作:
``` ```
┌── Kiro CLI / Claude CLI (MCP client) ──┐ ETTerms GUI(單一行程,唯一開 COM3 的人)
│ AI 呼叫工具:serial_open / write...
└───────────────┬─────────────────────────┘ ├─ Tab1: Serial 連線 ── SerialChannel 實體持有 COM3
│ stdio (JSON-RPC 2.0) │ TerminalView 即時顯示(AI 的 TX 以 [AI] 標色,RX 照常顯示)
┌───────────────┴─────────────────────────┐
│ ETTerms.SerialMcp (常駐行程) │ ├─ SerialBridgeServer(本機 named pipe: \\.\pipe\etterms-serial
背景 reader 累積 RX → serial_read 取出 │ ▲ write 轉發 / RX 廣播
│ │ System.IO.Ports.SerialPort │ │ │
└────────┼─────────────────────────────────┘ └─ Tab2: PowerShell (ConPTY) 跑 kiro-cli
│ ← COM 互斥:與 GUI 不可同開同一 port → └─ kiro 啟動子行程 ETTerms.SerialMcpstdio / JSON-RPC
[ 實體 COM port / UART / 開發板 ] └─ 不開 COM,連上面的 pipe → 收發都流經 GUI 的 channel
``` ```
**資料流:**
- **AI 送資料:** `serial_write` → pipe → GUI `SerialChannel.Write()` 送出 COM3**同時把這段 echo 進 Tab1 TerminalView`[AI]` 標色)** → 使用者看得到 AI 打了什麼。
- **裝置回資料:** COM3 RX → GUI 一邊顯示在 Tab1、一邊經 pipe 廣播 → `serial_read` 取出 → AI 讀到。
因為 RX/TX 物理上都流經 GUI 的 channel,**使用者在 GUI 看到的就是 AI 看到的**,完全同步。
### 暴露的工具 ### 暴露的工具
| 工具 | 參數 | 說明 | | 工具 | 參數 | 說明 |
|------|------|------| |------|------|------|
| `serial_list` | — | 列出可用 COM port`SerialPort.GetPortNames()` | | `serial_list` | — | 列出 **GUI 目前開著的 serial session**(名稱 + COM port + baud),供 AI 選定要操作哪一個 |
| `serial_open` | portName, baudRate, dataBits, parity, stopBits, handshake, newLine | 開啟並持有 port(啟動背景 reader 累積 RX | | `serial_attach` | portName \| sessionName | 經 pipe 綁定到 GUI 某個已開啟的 serial session(之後 read/write 都對它 |
| `serial_write` | text, appendNewLine? | 送出文字(可選附加換行 | | `serial_write` | text, appendNewLine? | 經 pipe 請 GUI 對綁定的 session 送出文字(GUI 同步 echo 到 TerminalView |
| `serial_read` | waitFor?, timeoutMs? | 取出 RX 緩衝;可等待特定字串或逾時 | | `serial_read` | waitFor?, timeoutMs? | 取出該 session 累積的 RX;可等待特定字串或逾時 |
| `serial_close` | — | 關閉 port | | `serial_detach` | — | 解除綁定(**不關閉 GUI 的 port**GUI 仍持有) |
> 與舊版規劃的差異:不再有「MCP 自己 `serial_open` / `serial_close` 實體 port」;改為 `serial_attach` / `serial_detach` 綁定 / 解除 GUI 既有 session。port 的開關一律在 GUI 操作。
### named pipe 協議(精簡)
- **傳輸:** Windows named pipe`\\.\pipe\etterms-serial`),純本機、不開網路埠。
- **訊息:** 換行分隔的 JSON,例:`{"op":"write","session":"COM3","data":"...","newline":true}` / `{"op":"read","session":"COM3","waitFor":"OK","timeoutMs":3000}` / `{"op":"list"}`
- **RX 推送:** GUI 主動把 RX 以 `{"op":"rx","session":"COM3","data":"..."}` 推給已連線的 clientclient 端累積成 buffer 供 `serial_read` 消費。
- **找不到 session** AI `attach` / `write` 一個 GUI 沒開的 port 時,回明確錯誤(要求使用者先在 GUI 開啟)。
### 設計重點 ### 設計重點
- **技術:** .NET 8 console`net8.0`,無 WinForms+ 官方 C# MCP SDK`ModelContextProtocol`),stdio / JSON-RPC 2.0 - **技術:** `ETTerms.SerialMcp` .NET 8 console`net8.0`,無 WinForms+ 官方 C# MCP SDK`ModelContextProtocol`),stdio / JSON-RPCGUI 端的 `SerialBridgeServer``System.IO.Pipes`
- **參數語意** 直接用 `System.IO.Ports.SerialPort`,與主程式的 `SerialSettings` 同一組參數(PortName / BaudRate / DataBits / Parity / StopBits / Handshake / NewLine - **port 擁有權單一化** 實體 `SerialPort` 只有 GUI 開,根除「兩個行程搶同一 COM」的問題
- **COM 互斥:** MCP server 開了某 port 時,ETTerms GUI 不可同時開同一 port(反之亦然)——一個 COM 同時只能被一個行程開啟 - **可視性:** AI 的 TX 在 GUI 以 `[AI]` 標色,與使用者手打的輸入區分;RX 兩邊同源
- **安全:** 本機、無雲、不碰 credential僅操作硬體 serial。 - **安全:** 本機、無雲、不碰 credentialpipe 僅限本機行程,只搬 serial bytes
### 註冊(Kiro CLI ### 註冊(Kiro CLI
@@ -396,7 +417,7 @@ kiro-cli mcp add --name serial --command dotnet `
--args "run --project src\ETTerms.SerialMcp\ETTerms.SerialMcp.csproj" --args "run --project src\ETTerms.SerialMcp\ETTerms.SerialMcp.csproj"
``` ```
或寫進 agent.json 的 `mcpServers`Claude CLI 則用其對應的 `mcpServers` 設定。註冊後直接對 AI 說「列出 COM port、開 COM3 115200、送 AT 看回應」即可 或寫進 agent.json 的 `mcpServers`Claude CLI 則用其對應的 `mcpServers` 設定。**使用前提:先在 ETTerms GUI 開好要操作的 serial 連線**,AI 才能 `serial_attach` 上去。註冊後即可對 AI 說「列出目前 serial session → 接上 COM3 → 送指令看回應」。
--- ---
@@ -563,14 +584,16 @@ dotnet publish src\ETTerms\ETTerms.csproj -c Release -r win-x64 --self-contained
- [ ] `dotnet publish` + Inno Setup / MSIX 安裝程式(待使用者指示) - [ ] `dotnet publish` + Inno Setup / MSIX 安裝程式(待使用者指示)
**驗收條件:** ✅ PDU 指令可用;Settings 重啟保留;Local Shell 行為正常(ConPTY);SFTP 可瀏覽遠端目錄。打包待後續。 **驗收條件:** ✅ PDU 指令可用;Settings 重啟保留;Local Shell 行為正常(ConPTY);SFTP 可瀏覽遠端目錄。打包待後續。
### Phase 9 — AI 整合:Serial MCP Server(工作量:S)🔜 規劃中 ### Phase 9 — AI 整合:Serial MCP ServerGUI 橋接模式)(工作量:M)🔜 規劃中
**目標:** 提供獨立 stdio MCP server讓 Kiro CLI / Claude CLI 等 AI agent 直接對 serial port 下指令、讀輸出 **目標:** 讓 Kiro CLI / Claude CLI 等 AI agent 收發 serial,且**使用者能在 GUI 即時看到 AI 的收發**。COM port 由 GUI 唯一持有,MCP 經本機 named pipe 橋接(見 [AI / MCP Integration](#ai--mcp-integrationserial-mcp-server)
**包含:** **包含:**
- [ ] GUI`Sessions/SerialBridgeServer.cs`——本機 named pipe server,暴露 list / attach / write / read,並把 serial session 的 RX 廣播給 client
- [ ] GUITerminalView 支援把 MCP 來源的 TX 以 `[AI]` 標色 echo,使用者可區分 AI 與手打輸入
- [ ] `src/ETTerms.SerialMcp/`.NET 8 console + 官方 C# MCP SDK`ModelContextProtocol`),stdio / JSON-RPC - [ ] `src/ETTerms.SerialMcp/`.NET 8 console + 官方 C# MCP SDK`ModelContextProtocol`),stdio / JSON-RPC
- [ ] 工具:`serial_list` / `serial_open` / `serial_write` / `serial_read`(含 `waitFor` + `timeout`/ `serial_close` - [ ] `SerialBridgeClient.cs`:連 GUI pipe、轉發 write、累積 RX
- [ ] 常駐持有 COM port + 背景 reader 累積 RX(跨呼叫保留狀態、可收 async 輸出) - [ ] 工具:`serial_list` / `serial_attach` / `serial_write` / `serial_read`(含 `waitFor` + `timeoutMs`/ `serial_detach`
- [ ] 註冊說明(`kiro-cli mcp add` / agent.json `mcpServers`)寫入 README - [ ] 註冊說明(`kiro-cli mcp add` / agent.json `mcpServers`)寫入 README,含「需先在 GUI 開好 port」前提
**驗收條件:** 在 Kiro CLI 註冊後,能透過 AI 對話「列 COM port → 開 COM3 115200 → 送指令 → 讀回應」完成一輪 serial 互動;同一 COM port 不與 GUI 同時開啟 **驗收條件:** GUI 開一條 SerialCOM3)→ 另一分頁 PowerShell 跑 kiro → AI 經 MCP `serial_attach` COM3 → `serial_write` 送指令、`serial_read` 讀回應,**整個過程在 GUI Tab1 即時可見(AI 的 TX 有 `[AI]` 標色)**;全程只有 GUI 開該 port
--- ---
+4 -4
View File
@@ -8,7 +8,7 @@ ETTerms 是一個 **C# .NET 8 WinForms** 的原生 Windows 終端機工作台,
**開發策略:GUI 先行** — 先把視窗外殼 + 分頁 + 連線清單做出來,再逐步補 Serial → SSH → VT100 → 腳本引擎 → Settings/About → PDU/Shell/SFTP。 **開發策略:GUI 先行** — 先把視窗外殼 + 分頁 + 連線清單做出來,再逐步補 Serial → SSH → VT100 → 腳本引擎 → Settings/About → PDU/Shell/SFTP。
**進度:** Phase 15 ✅、Phase 6 ✅(TTL 引擎 + Group 同步,SSH 待驗收)、Phase 7 ✅(Settings/About)、Phase 8 ✅(PDU + Shell/ConPTY + SFTP + Settings 擴充)、Phase 9 🔜(Serial MCP server,讓 AI 直接操作 serial)。打包待指示。 **進度:** Phase 15 ✅、Phase 6 ✅(TTL 引擎 + Group 同步,SSH 待驗收)、Phase 7 ✅(Settings/About)、Phase 8 ✅(PDU + Shell/ConPTY + SFTP + Settings 擴充)、Phase 9 🔜(Serial MCP server**GUI 持有 COM portMCP 經本機 named pipe 橋接**,AI 收發的資料即時顯示在 GUI)。打包待指示。
**v0.1.2** 新增 `sprintf2`TeraTerm 相容 C printf 格式化);`wait` 改為命中關鍵字後須等裝置安靜(`SettleMs` 預設 300ms)才接受並取「最後一次」出現,排除輸出中途的指令回顯(避免腳本搶跑)。 **v0.1.2** 新增 `sprintf2`TeraTerm 相容 C printf 格式化);`wait` 改為命中關鍵字後須等裝置安靜(`SettleMs` 預設 300ms)才接受並取「最後一次」出現,排除輸出中途的指令回顯(避免腳本搶跑)。
@@ -23,7 +23,7 @@ ETTerms 是一個 **C# .NET 8 WinForms** 的原生 Windows 終端機工作台,
- **連線儲存:** SQLite`Microsoft.Data.Sqlite` - **連線儲存:** SQLite`Microsoft.Data.Sqlite`
- **密碼儲存:** Windows Credential Manager(不落地明碼) - **密碼儲存:** Windows Credential Manager(不落地明碼)
- **PDU** SnmpSharpNetiPoMan II/III via SNMP - **PDU** SnmpSharpNetiPoMan II/III via SNMP
- **AI / MCP(選用):** 獨立 stdio MCP server`ETTerms.SerialMcp`,官方 C# SDK `ModelContextProtocol`把 serial port 暴露給 Kiro CLI / Claude CLI - **AI / MCP(選用):** stdio MCP server`ETTerms.SerialMcp`,官方 C# SDK `ModelContextProtocol`。**不自己開 COM port**,而是經本機 named pipe 接上 GUI 持有的 serial session,把 serial 收發暴露給 Kiro CLI / Claude CLIAI 的 TX/RX 同步顯示在 GUI
- **設定持久化:** JSON → `%LocalAppData%\ETTerms\settings.json` - **設定持久化:** JSON → `%LocalAppData%\ETTerms\settings.json`
## 常用指令 ## 常用指令
@@ -57,7 +57,7 @@ kiro-cli mcp add --name serial --command dotnet --args "run --project src\ETTerm
- 🚫 **不嵌 TeraTerm、不依賴 com0com** —— ETTerms 走全原生(這是與舊版 MyTeraTerm 的關鍵差異)。 - 🚫 **不嵌 TeraTerm、不依賴 com0com** —— ETTerms 走全原生(這是與舊版 MyTeraTerm 的關鍵差異)。
- 🚫 不要把 `For_AI/` 內容 commit 進 git。 - 🚫 不要把 `For_AI/` 內容 commit 進 git。
- ⚠️ Serial COM port 同時只能被一個 session 開啟,開啟前檢查可用性。 - ⚠️ Serial COM port 同時只能被一個 session 開啟,開啟前檢查可用性。
- ⚠️ Serial MCP server`ETTerms.SerialMcp`與 GUI **不可同時開同一個 COM port**AI 操作該 port 前,先關掉 GUI 對它的連線(反之亦然)。 - ⚠️ COM port 由 **GUI 唯一持有**Serial MCP server`ETTerms.SerialMcp`**不自己開 port**,而是經本機 named pipe 接上 GUI 已開啟的 serial session 來收發。AI 操作該 port 必須已在 GUI 開啟(pipe 找不到對應 session 就回錯誤)。
- ⚠️ VT 相容性以常見情境(VT100 / 常見 ANSI)為主,冷門 escape 後補,不阻塞 GUI 進度。 - ⚠️ VT 相容性以常見情境(VT100 / 常見 ANSI)為主,冷門 escape 後補,不阻塞 GUI 進度。
- ⚠️ 本專案**無伺服端祕密 / 無 DB 密碼 / 無 EC2 / 無 VM**,因此不套用 AWS / VirtualBox 部署流程。 - ⚠️ 本專案**無伺服端祕密 / 無 DB 密碼 / 無 EC2 / 無 VM**,因此不套用 AWS / VirtualBox 部署流程。
- ⚠️ Group 同步指令(`waitall` / `sendlnall` / `sendlngroup`**只能在 Run Group 模式**使用;`▶ Script``▶ Run All` 須拒絕含這些指令的腳本。 - ⚠️ Group 同步指令(`waitall` / `sendlnall` / `sendlngroup`**只能在 Run Group 模式**使用;`▶ Script``▶ Run All` 須拒絕含這些指令的腳本。
@@ -65,6 +65,6 @@ kiro-cli mcp add --name serial --command dotnet --args "run --project src\ETTerm
## 資料夾用途 ## 資料夾用途
- **`src/ETTerms/`** — 主應用程式(WinForms 視窗外殼 + 連線 / 終端機 / 腳本引擎)。 - **`src/ETTerms/`** — 主應用程式(WinForms 視窗外殼 + 連線 / 終端機 / 腳本引擎)。
- **`src/ETTerms.SerialMcp/`** — 🔜 獨立 stdio MCP server(給 AI agent 直接操作 serial,與 WinForms 主程式各自獨立行程net8.0 console + `ModelContextProtocol` SDK。 - **`src/ETTerms.SerialMcp/`** — 🔜 stdio MCP server(給 AI agent 收發 serial。獨立行程,但**不直接開 COM port**:經本機 named pipe 連到 GUI 的 `SerialBridgeServer`,由 GUI 代為讀寫實體 portnet8.0 console + `ModelContextProtocol` SDK。
- **`For_AI/`** — AI 協作素材與**參考專案**(`KKTerm-main` UI 參考、`MyTeraTerm` Script 參考)。整個資料夾 gitignored,僅供開發對照。 - **`For_AI/`** — AI 協作素材與**參考專案**(`KKTerm-main` UI 參考、`MyTeraTerm` Script 參考)。整個資料夾 gitignored,僅供開發對照。
- 本專案**無 `secret/` 資料夾**:沒有伺服端祕密 / DB 密碼 / compile-time secret,連線密碼一律走 Windows Credential Manager。 - 本專案**無 `secret/` 資料夾**:沒有伺服端祕密 / DB 密碼 / compile-time secret,連線密碼一律走 Windows Credential Manager。