Skip to Content
AI 協作(MCP)使用範例

使用範例

九個情境,由簡單到完整。每個範例都有:你對 AI 說的話、AI 會用到哪些工具、你該注意看什麼, 以及兩張圖——左邊是 AI 工具的對話,右邊是同一時刻的 nexus 畫面(狀態列有 MCP:<n>,Log 有 [mcp] 開頭的行)。

範例用的 AI 工具是 Claude Code,換成別的 AI 工具,工具呼叫的內容一樣。 註冊方式見安裝與註冊。

標「示意」的範例(5 與 8)使用的是示範用的暫存器定義檔與一段示意性的韌體程式碼, 不是任何廠商的真實檔案;網站上不會出現廠商的定義檔內容。


範例 1:讀晶片 ID

你說:

用 nexus 讀一下目前這顆晶片 0x00 開始的 16 個暫存器,告訴我晶片 ID 是什麼。

AI 用到的工具: get_status(確認目前分頁與位址)→ get_commands(確認 rd 的用法)→ nexus_exec(rd 0x00 16)。

看什麼: AI 先查指令表才下指令,不是亂猜語法;nexus 的 Log 面板會依序出現 [mcp] rd 0x00 16 與 [mcp] -> ... 的結果行,內容和你自己下 rd 一模一樣。

AI 對話:呼叫 get_status、get_commands、nexus_exec 後解讀晶片 ID
AI 的對話
nexus 畫面:Log 面板出現 [mcp] rd 0x00 16 與結果,狀態列顯示 MCP:1
同一時刻的 nexus

範例 2:人機協作——你改值,AI 看見

你說:

我等一下會在 nexus 裡手動改 0x10 的值。你用 wait_event 等到 Log 出現寫入的紀錄, 然後讀快取確認改成了什麼。

AI 用到的工具: get_log(取得目前的序號)→ wait_event(source: log,等含 0x10 的新紀錄)→ get_reg_snapshot(讀快取確認)。

看什麼: 你在畫面上用 wr 0x10 0x85 改值的那一刻,AI 那邊的 wait_event 就回來了; 接著它讀到的快取值與你寫的一致。AI 全程沒有碰匯流排——它只是看 Log 與快取。你切到別的分頁 操作也不影響它。

AI 對話:wait_event 回傳你寫入的 Log 行,接著 get_reg_snapshot 確認值
AI 的對話
nexus 畫面:人手動輸入 wr 的 Log 行,狀態列顯示 MCP:1
同一時刻的 nexus

範例 3:dump diff——找出改動前後的差異

你說:

先 dump 一份目前的暫存器存成檔案當 baseline,我去按一下設備上的按鈕,之後你再跟 baseline 比對,告訴我哪些位元變了。

AI 用到的工具: nexus_exec(dump all 存 baseline,結果的 files[] 會列出檔案的絕對路徑)→ (等你操作)→ nexus_exec(dump diff <baseline 路徑>,它會讀回目前的值再逐位元組比對)→ read_file(把差異報告讀進對話)。 需要自己寫檔時用 write_file。

看什麼: 指令產生的檔案路徑會直接出現在工具回傳的 files 裡,AI 不用猜檔名; read_file 只讀得到 logs\ 底下的檔案。

AI 對話:dump 兩次、dump diff、read_file 讀差異報告
AI 的對話
nexus 畫面:dump diff 的結果出現在 Log 面板
同一時刻的 nexus

範例 4:AI 寫 Lua,跑起來,看結果,修正

你說:

寫一支 Lua:把 0x20 寫成 0x5A 再讀回來比對,不一致就判定失敗。存起來跑跑看, 失敗的話自己改到通過。

AI 用到的工具: write_file(存到 logs\mcp\verify.lua,回傳絕對路徑)→ nexus_exec (run <絕對路徑>)→ 讀回傳的 PASS/FAIL 摘要 → 有問題就改寫再 write_file → 重跑。 腳本卡住時它會用 nexus_abort 中止。

看什麼: 腳本是寫在 logs\mcp\ 裡的,你可以隨時打開來看它寫了什麼;Lua 腳本的結束摘要 (check() 的通過/失敗)就是它判斷成敗的依據。Lua 的寫法見Lua 自動化。

AI 對話:write_file、nexus_exec run、看到 FAIL、修正後重跑看到 PASS
AI 的對話
nexus 畫面:Log 面板顯示腳本執行與結束摘要
同一時刻的 nexus

範例 5:用暫存器定義檔輔助 debug(示意)

這個範例用的是示範用的暫存器定義檔,不是真實晶片的資料。

前提: 該分頁已載入暫存器定義檔(見面板裡的 REGDEF)。

你說:

解讀目前 bank 的暫存器,哪個欄位的值看起來不合理?不確定的地方直接說不確定。

AI 用到的工具: get_status(確認有載入定義檔)→ nexus_exec(dump 先把值讀進快取)→ get_regdef_decoded(用定義檔解碼:欄位名稱、列舉名稱、目前的檢視)。

看什麼: 沒有讀過的暫存器,AI 看到的是「沒有值」,不是 0;有「條件檢視」的暫存器, 如果決定檢視的那個暫存器沒讀過,檢視會標成 unknown,並套用預設檢視。AI 不會為了回答而去碰 匯流排——這就是為什麼它要先 dump。

AI 對話:get_regdef_decoded 的結果,含 unknown 的檢視與沒有值的欄位
AI 的對話(示範用定義檔)
nexus 畫面:REGDEF 面板顯示同一組欄位解碼
同一時刻的 nexus:REGDEF 面板

範例 6:UART——送出文字,等韌體回應

你說:

開著的 UART 面板上,送一個 "PING" 給裝置,等它回含 "PONG" 的一行,告訴我回了什麼。

AI 用到的工具: get_status(確認 UART 面板與埠已開)→ get_log(取得目前序號)→ nexus_exec(uart send "PING\n")→ wait_event(source: uart,contains: PONG)。

看什麼: uart send 與你按 UART 面板的巨集是同一條路徑;wait_event 在回應到達的當下就回來, 不需要 AI 一直輪詢。UART 紀錄的時間戳與 Log 是同一個時間基準,可以對照。

AI 對話:uart send 之後 wait_event 回傳含 PONG 的 UART 紀錄
AI 的對話
nexus 畫面:UART 面板顯示送出的 PING 與收到的 PONG
同一時刻的 nexus:UART 面板

範例 7:Plugin 面板——讀狀態、按按鈕

你說:

看一下目前 Plugin 面板上有哪些控件,把「Enable」勾起來,然後按「Apply」, 告訴我 Plugin 回報了什麼。

AI 用到的工具: get_plugin_widgets(列出控件與目前的值)→ set_plugin_widget (value: true 勾選;activate: true 按按鈕)→ get_log(用回傳的 cmd_id 讀 Plugin 因此寫的 Log)。

看什麼: AI 的操作和你用滑鼠點是同一條路徑,Plugin 無法分辨;控件的 id 不是座標, 所以不受視窗大小影響。使用 Canvas 自繪的 Plugin 面板沒有控件可讀,AI 會得到「不支援」的回覆。

AI 對話:get_plugin_widgets 列出控件,set_plugin_widget 勾選並按下按鈕
AI 的對話
nexus 畫面:Plugin 面板的控件狀態已改變,Log 有 [mcp] widget 的行
同一時刻的 nexus:Plugin 面板

範例 8:韌體 debug(示意)

最完整的情境:把 Claude Code 開在 MCU 韌體的原始碼資料夾,讓 AI 同時看得到韌體原始碼與 裝置實際的 UART log 與暫存器。這個範例的韌體片段與 UART 輸出是示意用的, UART 由模擬後端產生資料。

你說:

這個韌體資料夾是我們的 MCU 程式。用 nexus 掃一下 UART 埠、開起來收 log, 等它出現 "LINK LOST" 時,把當下的暫存器狀態抓下來。然後對照原始碼, 說明這個 log 是哪一行 printf 印的、當下裝置處於什麼狀態。

AI 的做法:

  1. nexus_exec:uart scan,再 uart <埠> <鮑率> 開埠收 log。
  2. 事件發生的當下,AI 一來一回是「秒」級,狀態早就變了。所以它寫一支 Lua (write_file):用 uart.expect("LINK LOST", 30000) 等到那一行,立刻 dump() 抓暫存器; 腳本以毫秒級反應。用 nexus_exec 跑 run。
  3. nexus_exec:uart export——大量 UART log 不逐筆傳進對話,而是匯出成 logs\ 下的檔案, AI 直接對檔案 grep,再到原始碼資料夾找對應的 printf。
  4. 需要寫入受保護的暫存器時,先 password <晶片> 解鎖,再用 rd/wr 驗證它的推論。

示意的 Lua(logs\mcp\capture.lua):

i2c.addr(0x58) local line = uart.expect("LINK LOST", 30000) -- 逾時回傳 nil if line then dump() -- 事件當下的 256 位元組 else log("warn", "no LINK LOST within 30 s") end

看什麼: 暫存器快照是「事件發生當下」的,不是 AI 回頭去讀的;uart export 回傳的檔案路徑 出現在 files 裡;AI 把 log 內容與原始碼對起來說明。

AI 對話:uart scan、寫 Lua、uart export、對照原始碼說明狀態
AI 的對話(示意用韌體與模擬 UART)
nexus 畫面:UART 面板的 LINK LOST,Log 面板有 [mcp] uart export 與 run
同一時刻的 nexus

範例 9:操作 TERM(UART/SSH 終端)

AI 可以看你的 TERM 面板畫面,也可以在裡面打字——但只限於你已經開好、連好的那一個。

先決條件(要你自己做): AI 不能替你連線,term open/term close 對 AI 一律被拒絕。 請先在要用的分頁放好 TERM 面板,並自己連上:

term open ssh demo-board

或序列埠:

term open uart COM11 115200

連上、看到 shell 提示字元之後,再交給 AI。沒連線時 term_read 會回 connected:false,term_send 會回 「the TERM is not connected」;分頁根本沒有 TERM 面板則兩者都回「no TERM panel」。

9.1 看畫面,幫我整理

你說:

看一下 nexus 的 TERM 現在畫面上有什麼,用兩三句話整理給我。

AI 用到的工具: term_read

{"tool": "term_read", "args": {}}

回覆(示意,合成資料;節錄,實際 lines 有 rows 筆,空列為 ""):

{ "tab": 0, "connected": true, "source": "ssh", "target": "demo-board", "cols": 100, "rows": 24, "cursor": {"row": 3, "col": 19}, "lines": [ "demo@demo-board:~$ ls", "firmware logs notes.txt", "demo@demo-board:~$ cat notes.txt", "demo@demo-board:~$ " ] }

lines 是畫面上每一列的純文字(沒有顏色,列尾空白已去掉,全形字只出現一次),cursor 是游標的列/欄(從 0 起算)。 只有「現在看得到的畫面」,沒有回捲的內容。

9.2 跑一個指令,讀結果

你說:

在 TERM 裡執行 uname -a,把結果告訴我。

AI 用到的工具: term_send → (稍等)→ term_read

{"tool": "term_send", "args": {"text": "uname -a\r"}}

回覆:{"sent": 9}。Enter 要寫成 JSON 的 \r。指令送出後輸出是非同步到達的,AI 會等一下再讀畫面:

{"tool": "term_read", "args": {}}
{"lines": ["demo@demo-board:~$ uname -a", "Linux demo-board 6.1.0-demo #1 SMP aarch64 GNU/Linux", "demo@demo-board:~$ "], "cursor": {"row": 2, "col": 19}}

(節錄。)AI 看到提示字元回來、游標停在新的一列,就知道指令跑完了。

9.3 查核心訊息有沒有錯誤

你說:

在 TERM 裡看 dmesg 最後 30 行有沒有 error 或 fail,有的話告訴我是哪幾行。

AI 送出 {"text": "dmesg | tail -n 30\r"},再 term_read 讀畫面並挑出可疑的列。輸出超過一個畫面時,AI 應該改用 dmesg | grep -i -E 'error|fail' | tail -n 20,讓結果留在畫面內。

9.4 卡住的指令:Ctrl+C

你說:

剛剛那個 ping 一直不停,幫我停掉。

Ctrl+C 是控制字元 0x03,在 JSON 裡寫成 \u0003:

{"tool": "term_send", "args": {"text": "\u0003"}}

再 term_read 確認提示字元回來了。其他常用的:Tab 補全 \t、離開 exit\r、Ctrl+D \u0004、方向鍵上 \u001b[A。

9.5 序列埠:在 getty 登入提示登入

你說:

TERM 連著板子的序列埠,停在 login 提示。用帳號 demo 登入,密碼我來輸入,你先送帳號就好。

AI 送 {"text": "demo\r"},用 term_read 確認出現 Password:,然後停下來等你自己在 TERM 面板打密碼。 不要把密碼交給 AI 去 term_send:雖然 Log 只記位元組數,但 AI 的對話紀錄裡會有密碼。

注意事項

  • 打字之後要再讀:term_send 回 sent 只表示位元組已排進傳輸佇列,輸出之後才會出現。讀到的畫面還沒變就稍等再 term_read, 不要連續重送同一個指令。
  • 序列埠的終端大小:Linux 序列埠 console 不會自動知道面板有多大。畫面折行怪怪的,請在 TERM 裡執行 stty rows <列> cols <欄>(數字看面板標題的 欄×列)或 eval $(resize)。SSH 連線會自動同步,不用管。
  • 很長的輸出:term_read 只回目前看得到的畫面。要完整的內容,請 AI 在 shell 裡先導向檔案、或使用 TERM 的 session 記錄 (回捲與 session 記錄功能上線後,檔案在 logs\ 底下),再用 read_file 讀。
  • 風險:term_send 等於 AI 直接在你登入的 shell 打字,包括 sudo、rm、reboot。nexus 不攔、不確認; Log 面板只看得到 [mcp] term_send 9 bytes(只記位元組數,不記內容,避免密碼進 Log),所以你要看實際做了什麼,請看 TERM 面板本身。 不想讓 AI 碰就不要核准 term_send 的呼叫,或用 mcp off。

之後

  • 想讓 AI 做更多事,先讓它呼叫 get_commands——它會看到你這個版本實際有哪些指令。
  • GPIO 沒有對應的指令,只能從 Lua 的 gpio.* 操作,請 AI 寫腳本。
  • Lua 的函式不在 get_commands 的指令表裡,完整參考見API 參考。AI 常踩的幾個坑:
    • uart.expect() 用的是 Lua pattern,[ ] ( ) . % - + * ? ^ $ 要用 % 跳脫,例如 "En%[0%]"。
    • run 回 rc 0 只代表腳本已入列;結果看 Log 最後一行:Script PASSED、Script FAILED、Script error 加 Script INCOMPLETE(執行期錯誤)或 Script ABORTED。
    • rd <位移> <數量> 的數量是十六進位:rd 0x00 16 讀的是 22 個位元組。
    • uart send 原樣送出文字,再附加該 UART 面板目前的 TX 換行模式(預設 CRLF,可在面板改;get_status 的 uart.tx_newline 會回報)。沒有指令能設定這個模式,要精確的位元組就用 Lua 的 uart.write("...")。HEX 模式下文字必須是成對十六進位;模式為 none 時 uart send "" 會被拒絕。
    • 已掛載的 widget Plugin 可能攔截部分指令(例如 tab 2);指令沒作用時先看 Log。
  • 回頭看概念與安全須知,確認你知道 AI 能做什麼。