A personal LINE ↔ Claude Code bridge, modeled on the official Telegram channel plugin's shape. Not affiliated with LINE Corp or Anthropic — this is a hand-written, unofficial plugin for one owner talking to their own Claude Code instance. It intentionally does not support groups/rooms or LINE's richer message types; it's the minimum needed for 1:1 DM chat.
Security properties:
- Every inbound webhook is signature-verified against your channel secret
(
@line/bot-sdk'svalidateSignature) — unsigned/forged requests are rejected with 401 before anything reaches Claude. - Unknown senders never reach Claude directly. They get a pairing code;
the OWNER must approve it by hand (
bun approve.ts <code>) on the machine running the bridge. There is no in-chat approval flow, so a message on LINE itself can never grant access — this is deliberate, see the warning inserver.ts's MCP instructions. - The webhook server binds to
127.0.0.1only. It is never meant to be reachable directly from the internet — put a reverse proxy with TLS in front of it (nginx + Let's Encrypt is the easiest route; see below).
- Bun —
curl -fsSL https://bun.sh/install | bash - A domain (or subdomain) pointed at this machine, with nginx + a TLS
cert (
certbot --nginx -d line.yourdomain.comis the standard path). LINE's webhook requires HTTPS; a bare IP or self-signed cert won't work.
- Go to the LINE Developers Console
- Create a Provider (if you don't have one)
- Create a Messaging API channel
- Under Messaging API tab:
- Disable "Auto-reply messages"
- Disable "Greeting messages"
- Issue and note a long-lived Channel access token
- Under Basic settings tab, note the Channel secret
Example nginx server block (adjust the domain and cert paths, or just
run certbot --nginx -d line.yourdomain.com after this to have it fill
in the TLS block automatically):
server {
server_name line.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:3010;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 5m;
}
listen 80;
}This mirrors the exact on-disk shape of Anthropic's own Telegram channel
plugin (verified by inspecting a real working install), just registered
under your own marketplace id instead of claude-plugins-official — that
id belongs to Anthropic and shouldn't be reused for an unofficial plugin.
# 1. Copy this whole repo into the plugin cache, under your own marketplace id
mkdir -p ~/.claude/plugins/cache/korit-local
cp -r /path/to/claude-line-channel ~/.claude/plugins/cache/korit-local/line
# (the copied folder must contain .claude-plugin/plugin.json, .mcp.json, server.ts, package.json — check they're there)
# 2. Register the plugin in ~/.claude/plugins/installed_plugins.json
# (create the file if it doesn't exist yet){
"version": 2,
"plugins": {
"line@korit-local": [
{
"scope": "user",
"installPath": "/home/YOU/.claude/plugins/cache/korit-local/line",
"version": "0.1.0",
"installedAt": "2026-01-01T00:00:00.000Z",
"lastUpdated": "2026-01-01T00:00:00.000Z"
}
]
}
}# 3. Enable it in ~/.claude/settings.json — add (or merge) this:{
"enabledPlugins": {
"line@korit-local": true
}
}Before editing
~/.claude/settings.json, back it up (cp settings.json settings.json.bak). A JSON syntax error there can break plugin loading for your whole Claude Code install, Telegram included if you use it. Validate the file (e.g.python3 -m json.tool settings.json) before starting a new session.
There may also be a supported extraKnownMarketplaces entry with a
local filesystem source (skipping the manual installed_plugins.json
edit) — this wasn't verified against a real install for this write-up,
so treat it as worth trying, not as the confirmed path. The steps above
are the ones actually confirmed against a working Telegram plugin install.
mkdir -p ~/.claude/channels/line
cat > ~/.claude/channels/line/.env <<'EOF'
LINE_CHANNEL_ACCESS_TOKEN=your_channel_access_token
LINE_CHANNEL_SECRET=your_channel_secret
EOF
chmod 600 ~/.claude/channels/line/.envOnce the plugin is enabled, starting claude should launch the bridge's
MCP server automatically (via .mcp.json), which starts the webhook
listener on 127.0.0.1:3010. Confirm nginx is proxying it, then in the
LINE Developers Console, under Messaging API, set:
Webhook URL: https://line.yourdomain.com/webhook
Enable "Use webhook".
Add the bot as a friend (QR code is in the console) and send it any message. It replies with a pairing code. On the machine running the bridge:
cd ~/.claude/plugins/cache/korit-local/line
bun approve.ts <code>Message it again — you're through, and it behaves like the Telegram
channel: <channel source="line" user_id="..." ts="..."> blocks arrive
in the Claude Code session, and Claude answers back through the reply
tool.
LINE's Messaging API has two ways to send a message:
- Reply API (uses the
replyTokenfrom an inbound webhook event) — free and unlimited, but the token is single-use and expires ~10 seconds after the message arrives. An LLM agent's response time usually exceeds this for anything beyond a trivial answer. - Push API — works anytime, but on LINE's free Official Account plan you get a small monthly free quota (historically around 200 messages/month); beyond that, sending requires upgrading to a paid plan.
This bridge never calls the Push API automatically. The reply tool
tries the reply token first; if that window has already passed, it queues
the message to disk (~/.claude/channels/line/outbox.json) instead of
spending push quota. The queue drains automatically and for free: the very
next message this user sends — about anything, it doesn't need to ask "are
you done?" — carries its own fresh reply token, and the bridge spends that
token flushing the queue (up to 5 queued messages per token, LINE's limit
on a single reply call) before the new message is even handed to Claude.
Net effect: replies can be delayed until you send your next message, but the account's push quota stays untouched, available for you to spend deliberately (a broadcast, an urgent alert, whatever you actually want it for) rather than being silently consumed by ordinary chat latency.
Reference: https://developers.line.biz/en/docs/messaging-api/pricing/
- Text-only inbound/outbound plus image and file attachment download.
No Flex messages, no group/room chat — add them yourself if needed,
following the same pattern as
@line/bot-sdk's docs. access.jsonlives in~/.claude/channels/line/access.json— back it up if you re-provision the machine, or you'll have to re-pair.