claude-channel-line
把 LINE Messaging API 串到 Claude Code 做雙向訊息互動的 channel plugin。
- Claude 可主動透過 LINE 發訊息給你
- 你在 LINE 傳的訊息會被推進 Claude session,由 Claude 看到並回覆
- 內建白名單(pair / allowlist / disabled 三種 policy)
- 使用者身份:每個白名單 user 可標 暱稱 / 稱謂 / 角色,Claude 會用你給的名字稱呼、依角色調整信任度
- 搭配你自管的常駐 tunnel(建議 Cloudflare Named Tunnel),webhook URL 永久固定。channel 本身只 listen 本機 port,不再代管 cloudflared 子進程
架構
LINE 用戶 ──► LINE Platform
│ HTTPS webhook
▼
Cloudflare Tunnel (HTTPS)
│
▼
localhost:8788 (Bun + Hono)
│ 簽章驗證 → access policy
▼
MCP notification ──► Claude Code session
│
▼
line_reply / line_push tool
│
LINE Platform ◄──────┘
Channel 是一個 MCP server 子進程(透過 claude --dangerously-load-development-channels 啟動),同時在本機跑 webhook server 接收 LINE 推送,並暴露 MCP tools 讓 Claude 對 LINE 發訊息。
Prerequisites
裝這幾個工具:
- Bun 1.x — 建議
npm install -g bun(或brew install oven-sh/bun/bun) - Claude Code 2.1.123+
cloudflared—brew install cloudflared- 一組 LINE Messaging API channel 的 access token 與 channel secret
- 一個指向 Cloudflare 的網域(例如
casper.tw),用來建立永久固定的 webhook URL
Concepts
LINE 帳號層級對照:
| 層級 | 說明 | |---|---| | Business ID | 你的 LINE 商業帳號 | | Provider | 在 Business ID 之下的「服務提供者」(一旦選定不能改) | | Messaging API channel | 對應一個官方帳號(Official Account),這就是 bot 本身。token / secret 在這裡取得 |
兩種容易混淆的設定:
- Channel access token(長期)→ 用來呼叫 LINE API 發訊息
- Channel secret → 用來驗證 LINE webhook 的簽章
Quick Start
你先把一條常駐的 Cloudflare Named Tunnel 跑起來(與 Claude 解耦、開機自動啟動),channel 只透過 LINE_PUBLIC_URL 指向它——URL 永久固定,LINE Console webhook URL 設一次就好。
> v0.3.0 起:channel 不再自動 spawn cloudflared。 過去由 channel 代管的 cloudflared 子進程,連線穩定度與 Claude session lifecycle 綁在一起,session 重啟時容易斷線。現在 tunnel 改由你自管成常駐服務,channel 只 listen localhost:8788。舊 .env 裡的 LINE_TUNNEL_MODE / LINE_TUNNEL_NAME 會被忽略,不需要移除。
1. 申請 LINE Messaging API channel
1. LINE Business ID 登入或註冊 2. LINE Official Account Manager 建立官方帳號 3. 帳號頁右上角 Settings → Messaging API → Enable(會自動建一個 Provider 與 channel) 4. Settings → Response settings:
- 「Chat (聊天)」→ Off(很多人卡這——詳見 Troubleshooting)
- 「Webhook」→ On
- 「Auto-response messages」→ Off
- 「Greeting messages」→ Off
5. 進入 LINE Developers Console → 點選剛建立的 channel:
- Basic settings 分頁底部複製 Channel secret
- Messaging API 分頁底部 Issue + 複製 Channel access token (long-lived)
2. 設 Cloudflare Named Tunnel(做完一次永久不用再動)
需要:Cloudflare 帳號 + 一個指向 Cloudflare DNS 的網域。
# 一、登入(瀏覽器跳出選網域、Authorize)
cloudflared tunnel login
# 二、建 tunnel(記下回傳的 tunnel ID)
cloudflared tunnel create line-bot-channel
# 三、把子網域路由到 tunnel
cloudflared tunnel route dns line-bot-channel line-bot.example.com
# 四、寫 ~/.cloudflared/config.yml:
cat > ~/.cloudflared/config.yml <<EOF
tunnel: <剛建的 tunnel id>
credentials-file: $HOME/.cloudflared/<tunnel-id>.json
ingress:
- hostname: line-bot.example.com
service: http://localhost:8788
- service: http_status:404
EOF
驗證 tunnel 設定 OK(先手動跑一次測試):
# 啟動 tunnel
cloudflared tunnel run line-bot-channel &
# 另外一個終端起一個假 server
( printf 'HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\ntunnel ok!\n' | nc -l 8788 ) &
sleep 1
curl -s https://line-bot.example.com/
# 看到 "tunnel ok!" 即通了
pkill -f "cloudflared tunnel run"
2-1. 把 tunnel 變成「永久性連線」(常駐服務)
⚠️ 重點:channel 不會幫你跑 cloudflared。你必須自己讓 tunnel 常駐——開機自動啟動、crash 自動重連、跟 Claude session 完全解耦。這樣 session 重啟也不會斷線。
推薦:裝成系統服務(macOS launchd / Linux systemd)
# cloudflared 會讀 ~/.cloudflared/config.yml,註冊成開機自動啟動、crash 自動重連的背景服務
sudo cloudflared service install
# 啟動服務
sudo launchctl start com.cloudflare.cloudflared # macOS
# sudo systemctl enable --now cloudflared # Linux
# 確認在跑(看到 connection registered / 4 條連線即 OK)
cloudflared tunnel info line-bot-channel
之後 cloudflared 就一直在背景跑、開機自動起,你完全不用再管它。要停掉:
sudo cloudflared service uninstall # 移除服務
替代方案:自管 LaunchAgent(不用 sudo、跟著登入的使用者跑)
在 ~/Library/LaunchAgents/com.line-bot.cloudflared.plist 放:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.line-bot.cloudflared</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/bin/cloudflared</string>
<string>tunnel</string>
<string>run</string>
<string>line-bot-channel</string>
</array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>StandardErrorPath</key><string>/tmp/cloudflared-line-bot.log</string>
</dict>
</plist>
launchctl load ~/Library/LaunchAgents/com.line-bot.cloudflared.plist
# 之後開機 / 登入就會自動起,crash(KeepAlive)也會自動重連
> cloudflared 路徑用 which cloudflared 確認(Apple Silicon 通常 /opt/homebrew/bin,Intel /usr/local/bin)。
最後手段:手動背景跑(只適合測試,重開機就沒了)
nohup cloudflared tunnel run line-bot-channel >/tmp/cloudflared-line-bot.log 2>&1 &
3. 安裝 plugin
claude plugin marketplace add wcc723/2026-line-bot-channel-mcp
claude plugin install line@line-bot-channel
claude plugin list # 應看到 line@line-bot-channel ✔ enabled
或一鍵:
curl -fsSL https://raw.githubusercontent.com/wcc723/2026-line-bot-channel-mcp/main/install.sh | bash
4. 寫 channel 設定檔
mkdir -p ~/.claude/channels/line
cat > ~/.claude/channels/line/.env <<EOF
LINE_CHANNEL_ACCESS_TOKEN=<貼你的>
LINE_CHANNEL_SECRET=<貼你的>
LINE_WEBHOOK_PORT=8788
LINE_PUBLIC_URL=https://line-bot.example.com
EOF
chmod 600 ~/.claude/channels/line/.env
LINE_PUBLIC_URL 就是你在步驟 2 設好的那條常駐 tunnel 的固定網域。channel 只用它來顯示 / 提示 webhook URL(<LINE_PUBLIC_URL>/webhook),實際的 tunnel 由你在步驟 2-1 跑成常駐服務。
5. 啟動 channel session
claude --dangerously-load-development-channels plugin:line@line-bot-channel
進去後 /mcp 應顯示 plugin:line:line · ✔ connected。/line:tunnel status 應看到 state: running、url 是你的 named domain。
6. 設定 LINE webhook URL(一次性)
LINE Console UI 偶爾 400,用 API 最穩:
TOKEN=$(grep '^LINE_CHANNEL_ACCESS_TOKEN=' ~/.claude/channels/line/.env | cut -d= -f2-)
WEBHOOK="https://line-bot.example.com/webhook"
curl -X PUT https://api.line.me/v2/bot/channel/webhook/endpoint \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"endpoint\":\"$WEBHOOK\"}"
curl -X POST https://api.line.me/v2/bot/channel/webhook/test \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" -d '{}'
# 預期:{"success":true,"statusCode":200,"reason":"OK","detail":"200"}
7. 加 bot 為好友、配對使用者
1. Console > Messaging API 分頁 > 掃 QR code 加好友 2. LINE 傳第一則訊息 → bot 自動回 6 位配對碼 3. Claude session:
/line:access pair 123456 nickname=Casper role=owner
4. 之後再傳訊息 → 直接進 Claude session、Claude 用「Casper」稱呼你
完成。要讓 Claude 主動傳訊息:在 session 直接叫它用 line_reply / line_push tool。
Tunnel 由你自管
從 v0.3.0 起,channel 不再代管任何 tunnel 子進程。它只 listen localhost:8788,對外的公開 URL 完全由你提供(LINE_PUBLIC_URL)。
為什麼?過去 channel 會在啟動時 spawn cloudflared(quick / named 模式),但子進程的連線跟 Claude session lifecycle 綁在一起:session 一重啟、cloudflared 跟著重連,trycloudflare 的 quick URL 還會變,造成斷線與訊息漏接。把 tunnel 抽出來跑成常駐服務後,它與 Claude 解耦,session 隨便重啟都不影響線路。
| 你的選擇 | URL 穩定度 | 怎麼跑 | |---|---|---| | Cloudflare Named Tunnel(推薦) | ✅ 永久固定 | 跑成常駐服務(見 Quick Start 步驟 2-1) | | ngrok / Tailscale Funnel / 其他 | 視工具而定 | 你自己起,把固定網域填進 LINE_PUBLIC_URL |
不論用哪種,原則一樣:先有一條常駐、URL 固定的 tunnel 轉發到 localhost:8788,再把該 URL 填進 LINE_PUBLIC_URL。
使用者身份(暱稱 / 稱謂 / 角色)
每個白名單 user 可選擇性帶三個欄位:
| 欄位 | 必填 | 用途 | 範例 | |---|---|---|---| | nickname | 否 | Claude 稱呼你用的名字 | Casper、A君 | | title | 否 | 你跟使用者的關係描述 | 助教、老師、媽媽 | | role | 是(預設 member) | 信任度。owner 完整信任、member 對破壞性操作會謹慎 | owner / member |
設好後 Claude 收到該 user 的訊息會:
- 看到
nickname→ 用暱稱稱呼(「Casper,剛剛你說的…」) - 看到
title→ 組合稱呼(「A 助教好」「老師你問的問題」) - 看到
role=member→ 對破壞性操作(rm、改 secret、push commit 等)保持謹慎
設定範例:
/line:access pair 123456 nickname=Casper role=owner
/line:access allow Uxxx... nickname="A君" title="助教" role=member
/line:access set Uxxx role=owner
/line:access set Uxxx nickname="" # 清掉 nickname
⚠️ nickname / title 是 advisory(純文字提示給 Claude 看),不是 auth。userId 才是真正的身份識別。
在另一台機器上安裝(migrate / 多機)
Repo 根有 install.sh 自動化所有可自動化的步驟。
一鍵腳本
curl -fsSL https://raw.githubusercontent.com/wcc723/2026-line-bot-channel-mcp/main/install.sh | bash
會做:
1. 檢查 brew、claude 是否已裝 2. 裝 bun、cloudflared(如果還沒) 3. claude plugin marketplace add wcc723/2026-line-bot-channel-mcp + install line@line-bot-channel 4. 偵測你 ~/.claude/channels/line/.env 是否已就位(有就驗欄位長度,沒有就提示要怎麼搬) 5. 提示要把 ~/.cloudflared/(named tunnel 的 config + credentials)一起搬過來,並重新跑成常駐服務 6. 印出「下一步啟動命令」
腳本不會碰 secrets——.env 跟 cloudflared credentials 你必須手動從舊機器安全搬過來(scp / AirDrop / 1Password / 加密 USB),不要走 email、git、Slack 等。
必須手動搬的兩組檔案
| 檔案 | 何時需要 | 怎麼搬 | |---|---|---| | ~/.claude/channels/line/.env | 永遠需要 | scp old:~/.claude/channels/line/.env ~/.claude/channels/line/.env<br>之後 chmod 600 | | ~/.cloudflared/config.yml + ~/.cloudflared/<tunnel-id>.json | 用 Cloudflare Named Tunnel 時需要 | scp old:~/.cloudflared/* ~/.cloudflared/<br>記得改 config.yml 裡 credentials-file 路徑,搬完在新機器重跑 sudo cloudflared service install |
LINE Console 的 webhook URL 不需要改(DNS CNAME 不變)。
⚠️ 不要兩台同時跑 channel server
access.json(白名單)在每台機器各自一份。兩台同時跑會:
- LINE webhook 由 cloudflared 隨機分流給任一台
- 配對狀態不同步
- 訊息可能漏掉某些 session
退役舊機器:
# 在舊機器
# 退出 Claude session(/quit)
sudo cloudflared service uninstall # 若是裝成系統服務
# 或:launchctl unload ~/Library/LaunchAgents/com.line-bot.cloudflared.plist # 若用 LaunchAgent
# 或:pkill -f "cloudflared tunnel run" # 若只是手動背景跑
要兩台輪流用:每次只在一台跑常駐 tunnel + 啟動 Claude session,DNS 不用動。
Environment Variables
| 變數 | 必填 | 預設 | 說明 | |---|---|---|---| | LINE_CHANNEL_ACCESS_TOKEN | ✅ | — | LINE long-lived channel access token | | LINE_CHANNEL_SECRET | ✅ | — | LINE channel secret(簽章驗證用) | | LINE_WEBHOOK_PORT | | 8788 | 本地 webhook server port | | LINE_PUBLIC_URL | | — | 你自管常駐 tunnel 的固定公開 URL(webhook 為 <此值>/webhook)。未設則只 listen port、不提示 URL | | LINE_API_BASE | | https://api.line.me | 測試時可指向 mock server | | LINE_STATE_DIR | | ~/.claude/channels/line | 設定 / allowlist / log 檔的位置 | | LINE_LOG_FILE | | <state>/server.log | 設 off 完全關閉檔案 log | | LINE_LOG_LEVEL | | info | debug / info / warn / error | | LINE_PID_FILE | | <state>/server.pid | channel 啟動時用 PID file 偵測並清掉前次孤兒 instance;設 off 完全停用 |
可寫在 ~/.claude/channels/line/.env,也可走 shell env 覆寫。
Slash Commands
| 指令 | 說明 | |---|---| | /line:configure | 看當前設定 | | /line:configure set-token <TOKEN> | 設定 channel access token | | /line:configure set-secret <SECRET> | 設定 channel secret | | /line:configure public-url <URL> | 設你自管常駐 tunnel 的固定公開 URL | | /line:access list | 顯示白名單(含 nickname/title/role)+ pendingPairs + policy | | /line:access pair <6-digit> [nickname=...] [title=...] [role=...] | 兌換配對碼,可同時設身份 | | /line:access allow <userId> [nickname=...] [title=...] [role=...] | 直接加白名單 | | /line:access set <userId> [nickname=...] [title=...] [role=...] | 更新已存在 user 的身份 | | /line:access remove <userId> | 移出白名單 | | /line:access policy <pair\|allowlist\|disabled> | 切 DM 政策 | | /line:tunnel status | 顯示 channel 看到的公開 URL / port(tunnel 由你自管) | | /line:tunnel url | 印當前 webhook URL(<LINE_PUBLIC_URL>/webhook) |
完整 access policy 行為見 ACCESS.md。
開發者:改 plugin code 後重新驗證
每次改完 code、commit + push 之後,Claude Code 不會自動拉新版(cache key 是 plugin 版本,沒 bump 不重抓)。要強制重抓:
claude plugin marketplace update line-bot-channel
claude plugin uninstall line@line-bot-channel
claude plugin install line@line-bot-channel
# 清掉前一個 session 留下的孤兒 bun(如果有)
lsof -nP -iTCP:8788 -sTCP:LISTEN -t | xargs -I{} kill {} 2>/dev/null
claude --dangerously-load-development-channels plugin:line@line-bot-channel
可以包成自己的 shell function 或 .command 檔讓流程自動化。
實時觀察 webhook 收到什麼:
tail -F ~/.claude/channels/line/server.log
E2E 測試
agents/e2e-tester.md 提供自動化測試 subagent,模擬 LINE 使用者驅動 channel 走完所有情境。
# 終端 1:起 mock LINE API
bun run tests/e2e/harness.ts up 9999
# 終端 2:起 channel(測試模式:API base 指向 mock,不需要真的 tunnel)
LINE_CHANNEL_ACCESS_TOKEN=test-token \
LINE_CHANNEL_SECRET=test-secret \
LINE_API_BASE=http://localhost:9999 \
LINE_PUBLIC_URL=http://localhost:8788 \
LINE_STATE_DIR=/tmp/line-e2e-state \
bun start
# 終端 3:開 Claude session 載入 channel,呼叫 e2e-tester agent
claude --dangerously-load-development-channels plugin:line@line-bot-channel
> 請呼叫 e2e-tester subagent 跑完所有情境
單元 / 整合測試
bun install
bun test # 全部
bun test tests/unit # 單元
bun test tests/integration # 整合
Troubleshooting
訊息不會進 Claude session(最常見)
依下列順序排查:
1. chatMode 不是 bot(OA Manager 的 Chat 沒關):
TOKEN=$(grep '^LINE_CHANNEL_ACCESS_TOKEN=' ~/.claude/channels/line/.env | cut -d= -f2-)
curl -s https://api.line.me/v2/bot/info -H "Authorization: Bearer $TOKEN"
要看到 "chatMode":"bot"。如果是 "chat",回 OA Manager → Response settings → Chat 切 Off。 2. server log 沒看到 event received:webhook 沒進到 channel。
- 檢查
/line:tunnel status的 url、確認跟 LINE Console webhook URL 一致 - 你的常駐 tunnel 是否在跑:
cloudflared tunnel info <你的 tunnel name>(看到 4 條 connection 即正常),或pgrep -f "cloudflared tunnel run" - 這是最常見的斷線點:tunnel 必須是常駐服務(見 Quick Start 步驟 2-1)。若你只在某個終端手動
cloudflared tunnel run,終端關掉或電腦休眠就會斷
3. server log 看到 event received 但 session 沒反應:通常是 plugin 沒裝(只用 --plugin-dir)或忘了 --dangerously-load-development-channels。確認 claude plugin list 有 line@line-bot-channel ✔ enabled、/mcp 顯示 connected。
Unable to reach the origin service ... connection refused
cloudflared 通了,但 channel server 沒在 listen localhost:8788。
- 檢查
lsof -nP -iTCP:8788 -sTCP:LISTEN - 沒東西在 listen → 沒啟動 Claude session、或 Claude 啟動但 MCP fail(
/mcp看狀態)
/mcp 顯示 line · ✘ failed 或 Failed to reconnect to line
v0.2.1+ 會在啟動時自動清掉前次留下的孤兒 instance(透過 ~/.claude/channels/line/server.pid),所以這個錯誤現在很罕見。如果還是看到:
1. server log 看自動清理結果:
tail -20 ~/.claude/channels/line/server.log
應出現 pidfile: cleaned previous instance pid=... 或 pidfile: stale ...。 2. port 8788 被非 channel 程式占住:
lsof -nP -iTCP:8788 -sTCP:LISTEN
看 COMMAND 不是 bun.exe,就是其他工具佔用,要自行處理。 3. 你在 plugin 的 git clone 目錄下起 claude(只有開發者會碰到):專案層 .mcp.json 跟 plugin MCP 搶 port,從別處啟動即可。
要強制完全停用自動清理,設 LINE_PID_FILE=off。
Webhook Verify 顯示 400 Bad request 或 {message:null,...}
這是 LINE Developers Console UI 的 bug,不是你的問題。用 API 改 webhook URL 繞過:
TOKEN=$(grep '^LINE_CHANNEL_ACCESS_TOKEN=' ~/.claude/channels/line/.env | cut -d= -f2-)
curl -X PUT https://api.line.me/v2/bot/channel/webhook/endpoint \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"endpoint":"https://your-url/webhook"}'
curl -X POST https://api.line.me/v2/bot/channel/webhook/test \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{}'
success: true 即驗證通過。
Webhook Verify 通過但實際傳訊息 channel 沒收到
通常是 chatMode = chat(見上)或「Auto-response messages: Enabled」搶走訊息。
收到「感謝您的訊息!很抱歉,本帳號無法個別回覆用戶的訊息」
這是 LINE OA 的預設自動回應模板,代表 chatMode 是 chat 或 Auto-response messages 開著。回 OA Manager 全關掉。
一直斷線 / session 重啟後 webhook 就不通
幾乎都是 tunnel 沒跑成常駐服務——它跟某個終端或 Claude session 綁在一起,那個一收掉、電腦一休眠 tunnel 就斷。
- 確認 tunnel 是獨立的常駐服務(見 Quick Start 步驟 2-1):
# 系統服務
sudo launchctl list | grep cloudflared # macOS
# 或 LaunchAgent
launchctl list | grep com.line-bot.cloudflared
# 連線健康度(看到 4 條 connection registered 即正常)
cloudflared tunnel info <你的 tunnel name>
- channel 不會幫你起 cloudflared(v0.3.0 起移除了自動 spawn)。
/line:tunnel status只反映你在.env設的LINE_PUBLIC_URL,不代表 tunnel 真的活著——要用上面的指令確認常駐服務。 - 確認
cloudflared在 PATH(which cloudflared),且 service 啟動時讀到正確的~/.cloudflared/config.yml。
Reply token expired
正常行為。line_reply 會自動 fallback 到 push API。要避免 fallback 就在收到訊息 30 秒內回。
Claude session 收到訊息但 Claude 沒反應
session 處於 idle 才會處理 channel 事件;如果 Claude 正在做其他事,事件會排隊。
Security
~/.claude/channels/line/.env、~/.claude/channels/line/access.json、~/.claude/channels/line/server.log已 chmod 600- 千萬不要把
.envcommit - token rotate:到 LINE Developers Console reissue access token,再
/line:configure set-token <新>,重啟 session - 使用者錯把 group/room 訊息送來 → channel 直接丟棄(v1 只支援 1-on-1 DM)
FAQ
Q:channel 是 24/7 service 嗎? A:不是。Channel 只在 Claude session 跑著的時候才接收訊息。session 結束就停了。
Q:reply token 為什麼有 30 秒限制? A:這是 LINE API 的設計。超時自動走 push(會吃月配額)。
Q:可以同時跑多個 LINE bot 嗎? A:v1 不支援。一個 channel session 對應一個 channel access token。
Q:為什麼用 Bun 不用 Node? A:對齊官方 telegram channel 範本(anthropics/claude-plugins-official);Bun 原生支援 TS、bun run 啟動會自動裝依賴。
Q:為什麼要 --dangerously-load-development-channels 不能直接 --channels? A:研究預覽期間 --channels 只認 Anthropic 維護的 allowlist。自製 channel 必須走 dev flag。
License
Apache-2.0









