yomi
OfficialYomi is a local MCP server that lets an AI agent fully interact with your LINE account by acting as a secondary device using LINE's native protocol — no official API or browser required. It handles E2EE (Letter-Sealing) decryption natively.
Authentication & Session
Passwordless secondary-device login (PIN-based)
Reading Messages
Fetch all unread conversations (
get_unread_digest), list all chats/groups with previews, paginate messages from any conversation, and download/decrypt image, video, audio, or file attachments
Sending Messages & Media
Send E2EE text (with @mentions and reply-quoting), images, files, audio, video, location pins, contact cards, and stickers
Stickers
List, search, and preview owned sticker packages before sending
Reactions & Message Actions
Add/cancel reactions, unsend your own messages, and mark conversations as read
Contacts & Groups
Find/list contacts, get group members, create/rename groups, invite/kick members, leave groups, add/block/unblock friends, and accept invitations
Local Cross-Conversation Search
Bulk-index messages into a local SQLite database with semantic embeddings, then run hybrid keyword (FTS5) + semantic search across all indexed conversations — fully local, nothing uploaded
Privacy & Scope Control
Exclude sensitive chats from indexing (and purge their existing data), include them again later, and view the current privacy denylist
Provides tools for reading LINE messages (unread digests, chat messages, media), sending messages and images, searching conversations locally, managing login sessions, and controlling privacy scope, all by connecting as a secondary device on your LINE account.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@yomiSummarize my unread LINE and tell me who's waiting on a reply."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Yomi (読み) — the personal LINE MCP server
Yomi is an open-source LINE MCP server for your personal account. Read, reply, send images, and search every conversation from Claude or any local AI agent — without a browser, bot account, or LINE's own client.
The name reads more than one way. 読み (yomi) — a reading: not just parsing your messages, but reading the situation, the way you do. 詠み (yomi) — to recite: it doesn't read in silence, it reads things back to you. And 黄泉 (yomi) — the unreachable realm; the very yomi in 黄泉帰り (yomigaeri), "returning from Yomi." Like a messenger moving between two realms, Yomi passes through sealing and silence to bring messages still readable deep inside your conversations back into the light. It does not summon history already lost; it helps what has not disappeared be seen again.
Unlike LINE's official Bot MCP server, which connects AI agents to a LINE Official Account through the Messaging API, Yomi connects to your existing personal LINE account and conversations as a secondary device.
Yomi speaks LINE's TCompact-over-HTTPS protocol directly, decrypts Letter-Sealing
(E2EE) messages and media, and exposes the result to any AI agent through a small
stdio MCP server. Point Claude Desktop (or any
MCP client) at it and the agent can catch up on your LINE the way you do — read,
reply, send an image, mention someone, and search your whole history locally — and,
with get_insight, read the situation: which conversations are waiting on your
reply, who ties your world together, and what has gone overdue.
No official API, no bot account, no webhook. Yomi logs in as a secondary device on your own account.
Guides: What is a LINE MCP server? · LINE MCP 是什麼?Yomi、官方 Bot 與桌面自動化的差異.
Unofficial. Yomi is an independent personal project, not affiliated with or endorsed by LINE. Using it may be against LINE's Terms of Service, and running an additional client on your account carries a risk of rate-limiting or suspension. It is intended for reading your own account. Use it at your own risk. See Disclaimer.
Getting started
Native Desktop preview
Yomi Desktop puts the LINE inbox and local agent workspace in one native app.
It bundles its own runtime: users do not install Node, open Terminal, clone
this repository, or configure YOMI_RUN_MJS. Native Desktop is separate from
the Claude Desktop MCPB extension described below.
Platform | General-user package | Signing status | Install experience |
macOS 14+, Apple Silicon |
| Developer ID signed and Apple notarized | Open DMG → drag Yomi to Applications → open Yomi |
Windows 10/11 x64 |
| Trusted OSS signing application in progress | Download remains unavailable until signing and installer smoke pass |
The macOS package has been tested through the real downloaded-app path:
quarantined DMG, install to /Applications, Gatekeeper assessment, first
launch, bundled-runtime message refresh, and no-terminal LINE login form. The
Windows installer contains the equivalent in-app phone/PIN login flow and is
tested on a native Windows runner before release.
Desktop builds are experimental and unofficial. LINE changes may break them, and running an additional client may put an account at risk. Prefer a test account and keep a current backup. Preview installers are published as GitHub pre-releases only after platform signing is available.
See the desktop release process, code-signing policy, and privacy policy.
MCP server and Desktop Extension
You need Node.js and a LINE account. Yomi runs locally
through npx; you do not need to clone this repository, install Bun, or build
anything. Run node --version first, and install the current LTS if the command
is missing or the version is unsupported.
Yomi's local search index uses Node's built-in node:sqlite, so it needs
v22.13 or newer — except v23.0–v23.3, which are newer than v22.13 yet still
lack that module (it was unflagged in both 22.13.0 and
23.4.0). Any current LTS is fine.
This is engines.node in package.json; the rest of Yomi runs on older Node,
but search, scope and capture do not.
⚠️ Yomi needs a client that runs it on your own machine. Cloud-only tools (ChatGPT, Claude.ai web) cannot run Yomi. Configure Yomi in Claude Desktop or Claude Code and it works in both chat and Cowork, because Desktop starts Yomi on your machine and Cowork's local sessions load it. Do not ask Cowork to install Yomi for you: Cowork's shell runs inside a throwaway VM, not on your machine, so anything it installs there is gone when the session ends. Follow the steps below yourself, in your own terminal.
Choose the client you actually use and follow only that section. Claude Code and Claude Desktop have separate MCP settings; configuring one does not configure the other.
The easiest path, and the only one that needs no command line at all. Claude Desktop ships its own Node runtime, so nothing else has to be installed.
Download the bundle for your machine from the latest release:
Machine
File
Windows (Intel/AMD)
yomi-win32-x64.mcpbMac (Apple Silicon)
yomi-darwin-arm64.mcpbLinux (x64)
yomi-linux-x64.mcpbIn Claude Desktop, open Settings → Extensions, and drag the downloaded file onto that page (or just double-click the file).
Review what it asks for, click Install.
Start a conversation and say "log in to LINE". Yomi shows a form, you enter your phone number, and you confirm on your phone. No terminal at any point.
This skips the config file entirely, which also sidesteps the Windows MSIX bug described below.
Note for Cowork users: do not ask Cowork to install Yomi for you. Cowork's shell runs inside a throwaway VM — not on your machine — and it will not type into your real terminal. Install the bundle yourself with the three clicks above; once installed, Cowork's local sessions can use Yomi like any other tool.
Find the full path to
npx:macOS / Linux: which npx Windows: where npxOpen the Claude Desktop config file:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json⚠️ Windows: the documented path may not be the one Claude actually reads. Claude Desktop ships as an MSIX package, whose filesystem is virtualized. The app reads its config from
%LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\claude_desktop_config.jsonwhile Settings → Developer → Edit Config opens the unvirtualized
%APPDATA%\Claude\file. They are two different files that never sync, so a correct Yomi config written to the documented path is silently ignored — no error, no log, Yomi simply never appears. This is claude-code#26073, still open. If Yomi does not show up after a restart, write the same config to theLocalCachepath above as well. (Not applicable to Claude Code, or to Desktop installed outside MSIX.)Add Yomi under
mcpServers, replacing the examplecommandwith the full path printed in step 1. On Windows, JSON requires each\in the path to be written as\\:
{
"mcpServers": {
"yomi": {
"command": "/opt/homebrew/bin/npx",
"args": ["@rikaidev/yomi"]
}
}
} For example, a Windows path may look like
"C:\\Program Files\\nodejs\\npx.cmd". Use the path reported on your own
machine rather than copying either example blindly.
⚠️ The
npxyou name here does not decide which Node runs Yomi. On macOS and Linuxnpxis a script beginning with#!/usr/bin/env node, so it runs whichevernodecomes first on Claude Desktop'sPATH— not the Node installed beside thenpxyou just pointed at. Desktop inherits itsPATHfrom the desktop session, which is often not your terminal's, and version managers (nvm, fnm, asdf, volta, …) put their ownnodeahead of everything else. Sowhich npxcan report a perfectly modern install while Yomi still starts on an old Node.The symptom is specific: logging in, reading and sending all work, but search, scope and capture fail, and the log says
No such built-in module: node:sqlite. Yomi prints the runtime it actually got in that error — read it rather than assuming.The fix is to give the server a
PATHwhose first entry holds a supportednode:"yomi": { "command": "<the npx path from step 1>", "args": ["-y", "@rikaidev/yomi"], "env": { "PATH": "<dir holding a supported node><sep><a base PATH>" } }Separator:
:on macOS/Linux,;on Windows. Keep a usable base after it (/usr/local/bin:/usr/bin:/binor your platform's equivalent) — this replaces the server'sPATHrather than prepending to it, andnpxneeds the ordinary tools.Naming an absolute
nodeincommanddoes not work, however plausible it looks:npxspawns the package's own bin as a child process and resolvesnodefromPATHagain for it, socommandonly picks who runsnpx, not who runs Yomi. SettingPATHis what actually reaches the server.
Configuring this with an AI agent? MCP configs are mostly written by agents
now rather than by hand, so the steps above are stated for a human reading their
own machine. An agent should resolve the values instead of emitting placeholders,
and this setup has one trap worth stating outright: naming npx in command
does not pin the runtime. Procedure:
Find a Node that actually has the module Yomi's index needs — do not infer it from a version number:
node -e 'require("node:sqlite")' && command -v node # POSIX node -e "require('node:sqlite')" && where node # WindowsIf it throws, that Node is unusable for search/scope/capture. Try the other installs (
which -a node,nvm ls,fnm list, …) until one passes; only then continue.Write that Node's directory into
env.PATH, per the JSON above, and keep a basePATHafter it. Resolve the directory on the target machine (dirnameof the path from step 1); do not copy an example out of this README — they are Apple Silicon Homebrew paths and wrong nearly everywhere.Do not instead put an absolute
nodeincommand. It reads like the stronger fix and is not one:npxspawns the package's bin as a child and re-resolvesnodefromPATHfor it, socommandonly decides who runsnpx. This was measured, not assumed — with a Node 20 first onPATH,command-pinning still gave the server Node 20.command -vmay also hand back a wrapper rather than a real binary — heap shims, local-firstnpxwrappers and version-manager stubs are all common, and some behave differently depending on the client's working directory. Do not try to reason your way to the "real" one. Step 3 settles it.Verify with a tool that touches the index.
yomi versionis not a verification — it prints a string without opening SQLite, so it succeeds on a Node that cannot run search. Callget_scope_policy: it needs SQLite and no LINE login. If it returns the policy text, the runtime is right. If it fails, the error names the Node that actually ran and what it needed.Fully quit and reopen Claude Desktop. Confirm Yomi is loaded under Settings → Developer before anything else: if Yomi is not listed there, Claude never read your config — on Windows, see the MSIX warning in step 2. Once it is listed, the tools are available in chat and in Cowork's local sessions alike.
You do not need to install Claude Code for this setup.
claude mcp add yomi -- npx @rikaidev/yomiStart a new claude session. The Yomi tools should appear automatically. This
command configures Claude Code only; it does not configure Claude Desktop.
This form leaves the runtime to PATH, which is fine as long as the node your
claude session resolves is a supported one — it is the same PATH you can see,
unlike Claude Desktop's. Check with node -e 'require("node:sqlite")'. If it
throws, or you would rather not depend on PATH at all, name the Node
explicitly:
claude mcp add yomi -e PATH="$(dirname "$(command -v node)"):$PATH" -- npx -y @rikaidev/yomiEither way, verify with get_scope_policy rather than yomi version: the
version command prints a string without opening SQLite, so it passes on a Node
that cannot run search.
Running Claude Code inside a clone of this repo? Spell the spec
@rikaidev/yomi@latest. Without a tag,npxlooks for a local bin first, and in this repopackage.jsondeclares"bin": {"yomi": ...}while nothing links it intonode_modules/.bin— sonpxskips the install and dies withsh: yomi: command not found. An explicit tag makes it fetch the published package. Only affects working copies of Yomi itself; everywhere else the untagged form is fine.
This standard config works in most MCP clients:
{
"mcpServers": {
"yomi": {
"command": "npx",
"args": ["@rikaidev/yomi"]
}
}
}Cursor Settings → MCP → Add new MCP Server → name it yomi, command type, value: npx @rikaidev/yomi
Or add to .cursor/mcp.json in your project root.
Add to .vscode/mcp.json:
{
"servers": {
"yomi": {
"command": "npx",
"args": ["@rikaidev/yomi"]
}
}
}Add to ~/.config/opencode/opencode.json:
{
"mcp": {
"yomi": {
"type": "local",
"command": ["npx", "@rikaidev/yomi"],
"enabled": true
}
}
}codex mcp add yomi npx @rikaidev/yomiOr add to ~/.codex/config.toml:
[mcp_servers.yomi]
command = "npx"
args = ["@rikaidev/yomi"]Add to cline_mcp_settings.json:
{
"mcpServers": {
"yomi": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@rikaidev/yomi"]
}
}
}Add to ~/.codeium/windsurf/mcp_config.json — same JSON as standard config.
amp mcp add yomi -- npx @rikaidev/yomiAdvanced settings → Extensions → Add custom extension → name yomi, type STDIO, command: npx @rikaidev/yomi
grok mcp add yomi -- npx @rikaidev/yomiFirst login
Once connected, tell the agent:
"Log into LINE — my number is +8869XXXXXXXX."
Approve the device on your phone (see Logging in), and:
"Summarize my unread LINE and tell me who's waiting on a reply."
Related MCP server: line-desktop-mcp
What you can do
Yomi exposes 20 tools over MCP (the ones worth naming; see the tables). Large
read results use token-efficient TOON;
small status and write results use compact JSON. Errors remain plain text and media
uses native MCP image/audio/resource content.
"Honest error" below means an explicit failure naming the problem (e.g.
missing_decrypt_material) — Yomi never fabricates a fallback, a placeholder, or a
fake success.
Reading
Tool | Does |
| One-shot: every conversation with unread messages, each with its latest messages, E2EE-decrypted, sender names resolved. Built for "summarize my unread and suggest next steps." Read-only — never marks anything read. |
| Chats/groups/rooms with unread counts and a decrypted last-message preview. Newest-active first. |
| One conversation, decrypted. Paginate deeper with a |
| Any decrypted attachment (image/video/audio/file). Honest error on non-media. |
| Friend-list lookup by name substring, or the full list. Raw LINE data — no fuzzy scoring, no affinity ranking. |
| Members of a persistent group. Ad-hoc rooms without a group record fail honestly rather than returning a fake empty list. |
Insight (read the situation, not just the messages)
Tool | Does |
| A compact context network over your local index — structure the agent reasons over, not a message dump. Returns |
Writing (these really send — not drafts)
Tool | Does |
| Sends an E2EE text message now (pairwise key for 1:1, group key for groups/rooms). Optional |
| Encrypts, uploads to LINE OBS, and sends an E2EE image now. Works for 1:1, groups, and rooms. One send per call. Honest failure if the key can't be resolved or the upload is rejected. |
| Sends a read receipt the other party can see. Explicit only — reading messages and background capture never mark anything read. |
Search (local, cross-conversation — LINE has no such primitive)
Tool | Does |
| Hybrid search across all indexed conversations. Auto-collects on first use, diversifies results across chats, and returns a small context window around each hit. Returns |
| Explicit bulk-index into the local DB + embed for semantic search. The only tool allowed to bulk-fetch; runs once per call, never on a timer. |
Scope & privacy (all work offline, no LINE session needed)
Tool | Does |
| Denylist a conversation and purge its already-indexed data. Not just future capture — a denylist that left old data in the index would be fake privacy. |
| Re-allow a conversation (does not restore purged data). |
| Show the denylist / the full privacy policy (read from |
Session
Tool | Does |
| Passwordless secondary-device login. See below. |
Logging in
Prerequisite: on your primary phone, enable 設定 › 我的帳號 › 允許自其他裝置登入 (Settings › Account › Allow login on other devices). Without it LINE never offers this device a sign-in prompt — this is the single most common reason a first login appears to hang.
⚠️ Single Desktop Session Limit: LINE allows only one desktop client session at a time per account. Because Yomi connects as a desktop secondary device (
DESKTOPMAC), logging into Yomi will sign out your official LINE Desktop app (and signing back into LINE Desktop will invalidate Yomi's session). You cannot use Yomi and the official LINE Desktop client simultaneously.
You only need to give the agent your phone number in E.164 form — it supplies the
region itself (e.g. TW for a +886 number) when it calls the tool.
Yomi drives LINE's passwordless (secondary-device) flow. How the login surfaces depends on your MCP client:
Clients with the MCP
elicitationcapability —loginprompts you for your phone/region, shows the PIN in a dialog, and blocks to completion. One call, no PIN relayed through the model. Your phone number never enters the transcript.Clients without it (e.g. Claude Desktop today) — two calls.
login(with your phone + region) returns the PIN in its result; enter it in LINE on your primary phone and approve the device; then calllogin_complete, which blocks until the phone confirms. The agent should calllogin_completeimmediately — it does the waiting, so there is nothing for you to report back.From a terminal —
npx @rikaidev/yomi loginruns the whole flow on stdout, PIN and all. Always available as a fallback.
The one deadline that matters is LINE's: you have about 3 minutes from when the PIN is shown to enter it and approve the device. Yomi's own client keeps listening for many minutes beyond that, so a slow phone is never the failure — only LINE's 3-minute code lifetime is.
Once you've logged in, the session — including a login certificate — is persisted (see below), and future logins skip the PIN entirely.
There is also an experimental MCP Apps UI (a
ui://yomi/logincard) for clients that render interactive views. It is spec-correct and renders under the MCP Inspector, but some hosts fetch the resource without completing the view handshake, so it is display-only and never on the critical path — the flows above always work regardless.
Sessions and credentials
Yomi owns its own login. On startup it calls resumeSession() once, reading the
LINE session from the macOS Keychain (service dev.rikai.yomi.credentials, account
line) and silently refreshing the token if needed.
First-party credentials. The passwordless login persists the auth token, refresh token, certificate, MID, and the E2EE keypair itself — then reads them back to verify the write actually landed. A login that can't be persisted fails loudly at login, not silently at the next restart.
Shared session. The session is stored in the canonical
dev.rikai.yomi.credentialskeychain entry (or local credential store), allowing Yomi MCP and Yomi Desktop to share the exact same LINE login session seamlessly.Platform note. On macOS the session lives in the login Keychain. On Linux and Windows Yomi currently falls back to a local JSON file — functional, but less protected than an OS secret store, and less exercised than the macOS path. Native secure-storage backends (libsecret / DPAPI) are planned; until then, treat a non-macOS install accordingly.
Every tool except login and the offline scope/search tools returns an honest error
when there is no session. Yomi is otherwise a pure query server — it never polls,
never backfills in the background; each tool call makes exactly the LINE requests it
needs, and collect_messages is the only path that fetches across many chats at once.
Search
LINE has no cross-conversation search; Yomi builds one locally. The index is a
gitignored SQLite database (data/search-index.db) in the repo — nothing leaves
your machine.
Search is hybrid by design. It always runs FTS5 keyword search (bm25 over
bigram-preprocessed text, so CJK substrings with no word boundaries are covered) and,
when embeddings exist, semantic search, then fuses the two ranked lists by
Reciprocal Rank Fusion. Pure semantic search silently drops exact matches that
live in un-embedded messages; pure keyword misses paraphrases; fusing both surfaces
an exact term and a meaning-match together. The response's mode field always
reports which methods contributed.
Semantic ranking keeps each message as its own vector. Testing against real indexed LINE conversations found that embedding overlapping conversation windows diluted the center message and reduced retrieval quality. Context is therefore added only after ranking: Yomi limits one chat from flooding the top results and expands each winner with two messages on either side so the agent sees the exchange that gave the hit meaning without weakening the embedding itself.
Semantic ranking uses Xenova/bge-small-zh-v1.5 (BAAI general embedding, small,
Chinese-primary but multilingual) via transformers.js. Embedding inference runs
fully in-process on CPU — your message text is never sent anywhere.
The one caveat is a one-time model download: on the first collect_messages or
search_messages, transformers.js fetches the model (~90 MB) from huggingface.co
and caches it locally; every run afterwards is fully offline. That first fetch is an
outbound HTTPS request to HuggingFace for the model weights — it carries your IP and
which model is being downloaded, but no message content and no LINE data. If you
need Yomi fully air-gapped, pre-populate the transformers.js cache (or point it at a
local model directory) before the first search so no network call is ever made.
The secondary-device reality (read before trusting "I can't get old messages")
Yomi runs as a secondary device on your account. That shapes what it can see:
Group chats without Letter Sealing are plaintext at the LINE server — full history and media, no restriction.
Group chats with Letter Sealing are E2EE, and there the epoch matters. Such a group is encrypted with a shared group key that rotates (on membership changes, or when a client provisions a new one). LINE only ever hands a device the current group key — there is no API to fetch a superseded one. So a secondary device decrypts messages from the epoch whose key it holds onward; messages sent under an older epoch — before Yomi obtained the current key — can come back undecryptable, even in a group whose earlier history it can read. This is inherent to Letter Sealing's per-epoch group keys, not a bug here. It can also cut sideways: the epoch your phone holds and the epoch Yomi holds need not be the same, so the two devices can each read a different slice of the very same group.
Installing Yomi does not itself rotate anything, so "before I installed Yomi" is the wrong boundary. Yomi only ever resolves an existing group key; it never registers (mints) one, because minting rotates the group's shared secret for every member and strands every message encrypted under the previous one. So a fresh install picks up the current key and reads back to the last rotation — which may predate the install by months. The boundary is the last rekey, not your install date. And once Yomi has seen an epoch it keeps that key, so a rotation after install leaves it able to read both sides. If a group comes back undecryptable all the way to today, that is not this limitation — please report it.
1:1 media uses the account-level E2EE keychain, which a secondary device fully possesses (LINE syncs it during pairing) — Yomi can decrypt 1:1 images/files it can see.
1:1 history backfill is the one real limitation. LINE does not hand a secondary device the past 1:1 message history the way it does for groups. Messages received while Yomi is connected decrypt normally; deep-scrolling into 1:1 history that predates pairing may come back empty. This is a LINE server-side restriction, not a bug here.
When decryption genuinely fails, Yomi returns an explicit missing_decrypt_material
error — never a fake card or placeholder. Silence is honest; a fabricated result is not.
Development
For contributors working on the Yomi source code (requires bun
or Node.js 24+ — .nvmrc pins the same 24 that CI installs, so nvm use in this
directory picks it up; note nvm does not read engines, which is why the two are
stated separately and guarded by a test):
bun install # install dependencies (or npm install)
bun run.mjs # run the stdio MCP server
bun run.mjs login # run the login flow in a terminal
npm run build # tsc --noEmit — type-check only (Yomi ships & runs from src/)
npm test # bun testThe build only type-checks and compiles; this repo does not talk to live LINE
servers as part of its own build. Connecting an MCP client and calling login is
what actually starts a session.
src/
line/ LINE protocol core: TCompact/Thrift codec, E2EE (Letter-Sealing,
group keys, media), Talk/Auth/Sync service clients, session state,
passwordless login flow.
auth/ Credential store (macOS Keychain, JSON-file fallback off-darwin).
search/ Local cross-conversation index (SQLite + FTS5) and the offline
embedding pipeline (transformers.js).
mcp/ The stdio server: tool schemas, handlers, the privacy-policy loader,
and the experimental MCP Apps login view (mcp/ui/).
util/ [TAG]-prefixed logger (stderr only — stdout is the MCP JSON-RPC stream).Everything Yomi writes for humans goes to stderr; stdout is reserved for the
MCP JSON-RPC stream. A stray console.log corrupts the protocol — don't add one on
any path the server can reach.
Privacy
By default Yomi indexes all your conversations into the local, on-device search
index so an agent can search across them — a capture-all default, opt-out per chat.
Nothing is ever uploaded; the only data that leaves your device is whatever the agent
itself surfaces in its replies. (The one non-LINE network call Yomi itself makes is the
one-time embedding-model download from HuggingFace on the first search — model
weights in, no message content out; see Search.) PRIVACY.md
is the canonical policy,
and Yomi surfaces that same text to the agent on connect (and via get_scope_policy)
so it can disclose the default before any bulk read.
Disclaimer
Yomi is an independent, unofficial personal project, built for learning and for accessing one's own LINE account. It is not affiliated with, authorized, or endorsed by LINE Corporation. "LINE" is a trademark of its respective owner.
Running Yomi may violate LINE's Terms of Service, and operating an additional client
on an account can result in rate-limiting or suspension of that account. Yomi is
intended for accessing your own account and data. You are solely responsible for
how you use it. The software is provided "as is", without warranty of any kind — see
LICENSE.
Acknowledgments
Yomi's LINE protocol implementation was written with reference to three open-source projects, whose field layouts, E2EE chunk ordering, request shapes, and Thrift definitions informed this independent implementation:
evex-dev/linejs (MIT) — request shapes and the Letter-Sealing E2EE payload layout.
DeachSword/CHRLINE (BSD-3-Clause) — protocol field layouts and the passwordless login flow.
er1ce/LINE-Protocol (Apache-2.0) — Thrift enum definitions (ContentType, MessageRelationType, ServiceCode).
Their copyright notices and full license texts are reproduced in NOTICE,
as their licenses require.
License
MIT
Available Tools
41 toolsaccept_invitationA
Accepts a group/chat invitation for your LINE account — you join the chat. Use for a group you were invited to (its chatId appears with invited status). One accept per call.
| Name | Required | Description | Default |
|---|---|---|---|
| chatId | Yes | LINE group/room MID whose invitation to accept, as returned by list_conversations. |
add_friendA
Adds a person to your LINE friends by their MID (e.g. from get_group_members or find_contact). One add per call.
| Name | Required | Description | Default |
|---|---|---|---|
| mid | Yes | MID of the person to add as a friend, e.g. from get_group_members. |
block_contactA
Blocks a contact for your LINE account — they can no longer message you. Reversible with unblock_contact. One block per call.
| Name | Required | Description | Default |
|---|---|---|---|
| mid | Yes | MID of the contact to block. |
cancel_reactionA
Removes this account's reaction from a LINE message. Undoes a react_message. One cancellation per call.
| Name | Required | Description | Default |
|---|---|---|---|
| messageId | Yes | LINE message id whose reaction to remove, as returned by get_chat_messages. |
collect_messagesA
Bulk-fetch recent messages from LINE conversations into Yomi's local cross-conversation search index (LINE has no native cross-chat search). Fetches up to perChat per chat (default 100) for chatIds, or all conversations when omitted, and best-effort embeds them for semantic search — re-running also repairs any messages still missing a vector. A background capture loop keeps the index current on its own, so call this only to force a reconcile or backfill specific chats. Undecryptable messages are skipped, not fabricated.
| Name | Required | Description | Default |
|---|---|---|---|
| chatIds | No | LINE chat/group/room MIDs to collect from, as returned by list_conversations. Omit to collect from all conversations. | |
| perChat | No | Maximum recent messages to fetch per chat (default 100). |
create_groupA
Creates a new LINE group/room immediately with the given members (no message is sent). chatType 0 = group (invitees must accept before joining), 1 = room (members added directly); default 1. Provide name and mids (initial members, e.g. from find_contact). One create per call.
| Name | Required | Description | Default |
|---|---|---|---|
| mids | Yes | MIDs of the initial members to add. | |
| name | Yes | Name for the new group. | |
| chatType | No | LINE chat type: 0 = group (invite-based), 1 = room (direct add). Default 1. |
exclude_chatsA
Add conversations to Yomi's privacy denylist. Excluded chats are (1) skipped by future collect_messages/search_messages auto-collect — never fetched or indexed — and (2) PURGED now: their already-indexed messages and embeddings are deleted from the local index in the same call. A real privacy action, not just a future filter. Local-index operation; works without a live LINE session.
| Name | Required | Description | Default |
|---|---|---|---|
| chatIds | Yes | LINE chat/group/room MIDs to exclude, as returned by list_conversations. |
find_contactA
Find LINE friends whose display name contains name (case-insensitive substring). Returns each match's mid for send_message. Raw friend-list lookup — no ranking, no fuzzy scoring.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Substring to match against friends' display names, case-insensitive. |
get_chat_messagesA
Fetch messages from one LINE conversation. Text is E2EE-decrypted when keys are available; each message has fromName (resolved sender), a mediaType flag (image/video/audio/file) plus messageId for get_message_media, and mentions (LINE's raw contentMetadata.MENTION, or null — a literal "@name" in text is not itself a mention). Without before, returns the most recent count. With before (id/deliveredTime of the oldest message already seen), returns one older page — repeat to page further back.
| Name | Required | Description | Default |
|---|---|---|---|
| count | No | Maximum number of messages to return (default 50). | |
| before | No | Cursor to fetch messages older than this point. Use the id and/or deliveredTime of the oldest message already seen. | |
| chatId | Yes | LINE chat/group/room MID, as returned by list_conversations. |
get_group_membersA
List the members of one LINE group (mid + displayName each). Resolves persistent groups (c...); ad-hoc rooms (r...) without a group record return an honest error, never a fabricated empty list.
| Name | Required | Description | Default |
|---|---|---|---|
| chatId | Yes | LINE group chat MID, as returned by list_conversations. |
get_insightA
A compact "what needs my attention" context network over the local index — you make the final call, this assembles the evidence cheaply. Nodes: connectors (people across ≥2 of your chats, with structural bridges) and relationships (per-conversation engagement, reply rhythm, recency). open: conversations whose latest message is NOT yours, ranked by how overdue they are relative to your usual reply rhythm there, each with fromName (last speaker), a preview of the latest message, overdueRatio/typicality, and a lastMessageId pointer. It carries NO message threads and makes NO judgement about addressee, nicknames, or open-request vs closing-ack — those are language understanding you do by reading each preview (a group message may be addressed to someone else, who then owns it), fetching the full thread with get_chat_messages only for the few worth it. Reads across all conversations (denylist-excluded dropped). Empty only when the index is empty.
| Name | Required | Description | Default |
|---|---|---|---|
| chatId | No | Optional focus: restrict `relationships` and `pending` to this chat (as returned by list_conversations). Omit to scan all conversations. | |
| sinceHours | No | Lookback window in hours, measured back from the newest captured message (not wall-clock). Default 504 (21 days). |
get_message_imageA
Download and decrypt one LINE image message. Legacy alias of get_message_media restricted to images; prefer get_message_media for video/audio/file.
| Name | Required | Description | Default |
|---|---|---|---|
| chatId | Yes | LINE chat/group/room MID the message belongs to. Required to locate E2EE key material. | |
| preview | No | Fetch the smaller preview object instead of the full-resolution original. | |
| messageId | Yes | LINE message id, as returned by get_chat_messages. |
get_message_mediaA
Download and decrypt one LINE media message of any downloadable type (image, video, audio, file). Returns image/audio MCP content, or an embedded resource blob (with filename when known) for video/file. Non-media messages (text, sticker refs, unsupported types) return an honest error naming the content type — never fabricated bytes.
| Name | Required | Description | Default |
|---|---|---|---|
| chatId | Yes | LINE chat/group/room MID the message belongs to. Required to locate E2EE key material. | |
| preview | No | Fetch the smaller preview object instead of the full-resolution original (images/video only). | |
| messageId | Yes | LINE message id, as returned by get_chat_messages. |
get_scope_policyA
Return Yomi's data-capture privacy policy (the disclosure to show the user) plus the current list of excluded conversations. Call this to show the user, in concrete terms, what Yomi captures by default and how to exclude conversations.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
get_unread_digestA
One-shot unread digest: every LINE conversation with unread messages (all pages of the inbox, newest activity first), each with its most recent messages (default 10), E2EE-decrypted, with resolved sender names. Saves calling list_conversations then get_chat_messages per chat. Read-only: never marks anything read, never touches the search index; denylist-excluded conversations are omitted. Returns an empty list when nothing is unread — never fabricated.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of unread conversations to return, most recently active first (default: all of them). | |
| perChat | No | Maximum recent messages to include per unread conversation (default 10). |
include_chatsA
Remove conversations from Yomi's denylist, re-allowing future capture. Does NOT restore data purged when the chat was excluded — capture resumes from empty history going forward. Local-index operation; works without a live LINE session.
| Name | Required | Description | Default |
|---|---|---|---|
| chatIds | Yes | LINE chat/group/room MIDs to re-include. |
invite_memberA
Invites members into a LINE group immediately. Invitees must accept before joining. Provide mids (from find_contact or get_group_members). One call per invite batch.
| Name | Required | Description | Default |
|---|---|---|---|
| mids | Yes | MIDs of the people to invite. | |
| chatId | Yes | LINE group/room MID to invite into, as returned by list_conversations. |
kick_memberA
Removes (kicks) members from a LINE group immediately — they lose access at once (re-adding needs a fresh invite); visible to every member. Provide mids (from get_group_members). One call per removal batch.
| Name | Required | Description | Default |
|---|---|---|---|
| mids | Yes | MIDs of the members to remove. | |
| chatId | Yes | LINE group/room MID to remove members from, as returned by list_conversations. |
leave_groupA
Makes THIS LINE account leave a group immediately — it loses access to the group and its history. One leave per call.
| Name | Required | Description | Default |
|---|---|---|---|
| chatId | Yes | LINE group/room MID to leave, as returned by list_conversations. |
list_contactsA
List the authenticated user's full LINE friend list as-is (mid + displayName). No ranking, no interaction-frequency ordering.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
list_conversationsA
List LINE conversations (chats, groups, rooms) with unread counts, a preview of the last message, and a human-readable name (group title, or the other party's display name for a 1:1).
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of conversations to return (default 20). |
list_excluded_chatsA
List the conversations currently on Yomi's privacy denylist. Returns [{ chatId, name }]; name is best-effort resolved when a live LINE session exists, otherwise null — never a fabricated placeholder. Local-index operation; works without a live LINE session.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
list_stickersA
List the sticker packages this LINE account OWNS — the only stickers it can send. Returns each package's packageId (STKPKGID), title, and version (STKVER). Get individual sticker ids via search_stickers, then send_sticker. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| language | No | Locale for package titles, e.g. 'en' or 'zh-Hant'. Defaults to 'en'. |
loginA
Log in to LINE via the passwordless secondary-device flow. Requires 設定 > 我的帳號 > 允許自其他裝置登入 enabled on the primary phone — with it off LINE never prompts that phone and NO login can succeed, so raise this with the human up front rather than after a failure. On MCP clients with form elicitation, this call first confirms that setting, then prompts for phone/region and PIN and completes login by itself; if the human says the setting is off, it returns the enabling steps without starting a login (relay them verbatim, then call login again). On clients without it (e.g. Claude Desktop), phone/region come from the arguments or a persisted login, and this returns as soon as LINE issues the PIN (or reports none needed) — then call login_complete IMMEDIATELY (do not wait for the human). LINE gives ~3 minutes from PIN display to confirm on the phone; login_complete blocks past that, so calling it late only wastes that window.
| Name | Required | Description | Default |
|---|---|---|---|
| phone | No | Phone number in E.164 form, e.g. +8869XXXXXXXX. Omit to be prompted (elicitation) or to reuse a persisted number. | |
| region | No | Region code, e.g. TW, JP, TH, ID, US. Omit to be prompted (elicitation) or to reuse a persisted region. |
login_completeA
Finish a passwordless login that login started on a client without form elicitation (not needed on form-elicitation-capable clients). No arguments. Call immediately after login returns — do not wait for the human. Blocks while they enter the PIN (skipped if a stored certificate is valid) and approve the device, then returns the profile. LINE's real deadline is ~3 minutes from PIN display. Errors if no login is pending.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
mark_readA
Send a LINE read receipt (mark a conversation read up to messageId, or the latest when omitted) — a real action the other party can see. Use ONLY when the user explicitly wants to mark a chat read; reading (get_chat_messages, get_unread_digest) and background capture never mark read. Fails honestly if there is nothing to mark.
| Name | Required | Description | Default |
|---|---|---|---|
| chatId | Yes | LINE chat/group/room MID to mark read, as returned by list_conversations. | |
| messageId | No | Optional message id to mark read up to. Omit to mark read up to the latest message. |
preview_stickerA
Show sticker preview images (MCP image content from the public sticker CDN) so you and the user can SEE them before sending. Give a packageId (from list_stickers/search_stickers) to preview its first stickers, or add a stickerId to preview just one. Each image is labeled with its stickerId + packageId for send_sticker. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max stickers to preview when no stickerId is given (default 8). | |
| packageId | Yes | Sticker package id (STKPKGID) to preview. | |
| stickerId | No | Optional specific sticker id (STKID) to preview just that sticker. |
react_messageA
Adds a reaction to a LINE message, visible to the conversation. reactionType: 2 = 👍 LIKE, 3 = ❤️ LOVE, 4 = 😆 LAUGH, 5 = 😮 SURPRISE, 6 = 😢 SAD, 7 = 😡 ANGRY (default 2). One reaction per call.
| Name | Required | Description | Default |
|---|---|---|---|
| messageId | Yes | LINE message id to react to, as returned by get_chat_messages. | |
| reactionType | No | Predefined reaction: 2=👍LIKE, 3=❤️LOVE, 4=😆LAUGH, 5=😮SURPRISE, 6=😢SAD, 7=😡ANGRY. Default 2. |
rename_groupA
Renames a LINE group/chat immediately; the new name is visible to every member. Works on groups/rooms (chatId starting with c/r). One rename per call.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | New group name. | |
| chatId | Yes | LINE group/room MID to rename, as returned by list_conversations. |
search_messagesA
Search across your LINE messages. Hybrid ranking: FTS5 keyword search (covers every indexed message, so exact matches are never dropped) fused with semantic similarity when embeddings are available — the mode field reports which contributed (hybrid | semantic | keyword). If the index is empty and a session is live, it auto-collects all conversations first; a populated index searches locally with no network. Empty index with no session returns an honest notice, never a fabricated match list.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of results to return (default 20). | |
| query | Yes | Search query. Plain keywords and natural-language descriptions both work — keyword matching catches exact terms, semantic matching catches paraphrases. |
search_stickersA
Search the account's OWNED sticker packages by title and expand each match into its individual sticker ids (STKID), ready for send_sticker. Case-insensitive substring match on the package title. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max matching packages to expand with sticker ids (default 8). | |
| query | Yes | Substring to match against owned package titles (case-insensitive). | |
| language | No | Locale for package titles to match against, e.g. 'en' or 'zh-Hant'. Defaults to 'en'. |
send_audioA
Sends an E2EE audio message to a LINE conversation immediately (same pipeline as send_file). Provide exactly one of filePath or audioBase64; optional durationMs sets the recipient player length. Works for 1:1/group/room. One send per call.
| Name | Required | Description | Default |
|---|---|---|---|
| chatId | Yes | LINE chat/group/room MID to send to, as returned by list_conversations. | |
| fileName | No | Optional original filename (used for the upload name; defaults to the basename of filePath or audio.m4a). | |
| filePath | No | Local filesystem path to the audio file. Mutually exclusive with audioBase64. | |
| durationMs | No | Optional audio duration in milliseconds, for the recipient player progress bar. | |
| audioBase64 | No | Base64-encoded audio bytes. Mutually exclusive with filePath. |
send_contactA
Shares a LINE contact card to a conversation immediately — the recipient sees a tappable card for contactMid (e.g. from find_contact or get_group_members). displayName is optional (resolved from the mid when omitted). Works for 1:1/group/room. One send per call.
| Name | Required | Description | Default |
|---|---|---|---|
| chatId | Yes | LINE chat/group/room MID to send to, as returned by list_conversations. | |
| contactMid | Yes | MID of the person whose contact card to share, as returned by find_contact or get_group_members. | |
| displayName | No | Optional display name for the card. Resolved from contactMid when omitted. |
send_fileA
Sends an E2EE file attachment (any type — .docx, .pdf, .zip, …) to a LINE conversation immediately (same pipeline as send_image; the original filename is sealed end-to-end). Works for 1:1/group/room; fails honestly if the key cannot be resolved or the upload is rejected. One send per call. Provide exactly one of filePath or fileBase64; fileName is required with fileBase64 (and overrides the basename when given with filePath).
| Name | Required | Description | Default |
|---|---|---|---|
| chatId | Yes | LINE chat/group/room MID to send to, as returned by list_conversations. | |
| fileName | No | Original filename shown to the recipient (sealed E2EE). Required with fileBase64; optional with filePath (defaults to its basename). | |
| filePath | No | Local filesystem path to the file. Mutually exclusive with fileBase64. | |
| fileBase64 | No | Base64-encoded file bytes. Mutually exclusive with filePath; requires fileName. |
send_imageA
Sends an E2EE image to a LINE conversation immediately (encrypt → upload to OBS → send). Works for 1:1/group/room; fails honestly if the key cannot be resolved or the upload is rejected. One send per call. Provide exactly one of imagePath or imageBase64.
| Name | Required | Description | Default |
|---|---|---|---|
| chatId | Yes | LINE chat/group/room MID to send to, as returned by list_conversations. | |
| imagePath | No | Local filesystem path to the image file. Mutually exclusive with imageBase64. | |
| imageBase64 | No | Base64-encoded image bytes. Mutually exclusive with imagePath. |
send_locationA
Sends a location (map pin) to a LINE conversation immediately — latitude/longitude plus optional title (place name) and address. Works for 1:1/group/room. One send per call.
| Name | Required | Description | Default |
|---|---|---|---|
| title | No | Optional place name shown on the pin. | |
| chatId | Yes | LINE chat/group/room MID to send to, as returned by list_conversations. | |
| address | No | Optional address shown under the pin. | |
| latitude | Yes | Latitude in decimal degrees. | |
| longitude | Yes | Longitude in decimal degrees. |
send_messageA
Sends a text message to a LINE conversation immediately (not a draft). Always E2EE (pairwise for 1:1, group key for group/room); fails honestly rather than sending plaintext if the key cannot be resolved. One send per call. To @mention someone, put the visible "@name " into text AND pass a matching mentions entry — without mentions, "@name" is plain text and notifies no one. Resolve MIDs via get_group_members or find_contact first.
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | Plain-text message body, including the literal "@name" text for any mentions — mentions only mark up text that is already there. | |
| chatId | Yes | LINE chat/group/room MID to send to, as returned by list_conversations. | |
| mentions | No | Optional @mentions. Each entry marks a span of `text` as a mention of one user, which LINE highlights and notifies. Omit to send `text` as plain, non-notifying text. | |
| replyToMessageId | No | Optional message id (from get_chat_messages) this replies to — LINE renders a quoted reply. Omit for a normal message. |
send_stickerA
Sends a LINE sticker to a conversation immediately, named by stickerId (STKID) + packageId (STKPKGID). Only OWNED stickers can be sent — get ids from search_stickers/list_stickers. Works for 1:1/group/room. One send per call.
| Name | Required | Description | Default |
|---|---|---|---|
| chatId | Yes | LINE chat/group/room MID to send to, as returned by list_conversations. | |
| version | No | Sticker version (STKVER). Defaults to "1". | |
| packageId | Yes | LINE sticker package id (STKPKGID). | |
| stickerId | Yes | LINE sticker id (STKID). |
send_videoA
Sends an E2EE video to a LINE conversation immediately (same pipeline as send_file; uses LINE chunked video encryption so it plays and integrity-verifies on official clients). Provide exactly one of filePath or videoBase64; optional durationMs sets the scrubber length. Works for 1:1/group/room. One send per call.
| Name | Required | Description | Default |
|---|---|---|---|
| chatId | Yes | LINE chat/group/room MID to send to, as returned by list_conversations. | |
| fileName | No | Optional original filename (used for the upload name; defaults to the basename of filePath or video.mp4). | |
| filePath | No | Local filesystem path to the video file. Mutually exclusive with videoBase64. | |
| durationMs | No | Optional video duration in milliseconds, for the recipient player scrubber. | |
| videoBase64 | No | Base64-encoded video bytes. Mutually exclusive with filePath. |
unblock_contactA
Unblocks a previously blocked contact for your LINE account. Undoes block_contact. One unblock per call.
| Name | Required | Description | Default |
|---|---|---|---|
| mid | Yes | MID of the contact to unblock. |
unsend_messageA
Retracts (unsends) one of YOUR OWN LINE messages for everyone — deletes it from the conversation for all participants and CANNOT be undone. LINE allows unsending only your own messages. SAFETY GATE: you must pass confirm: true, or the call refuses so it can never fire by accident. One unsend per call.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | Yes | Must be true to proceed. Retraction is irreversible; the call refuses unless this is explicitly true. | |
| messageId | Yes | LINE message id to retract (must be your own), as returned by get_chat_messages. |
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v0.6.2- Changed
get_unread_digest1 field changed- changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of conversations to scan for unread (default 20)."New value: +"Maximum number of unread conversations to return, most recently active first (default: all of them)."
39 tool updates
v0.4.1- Added
accept_invitation - Added
add_friend - Added
cancel_reaction - Added
collect_messages - Added
create_group - Added
exclude_chats - Added
find_contact - Added
get_chat_messages - Added
get_group_members - Added
get_insight - Added
get_message_image - Added
get_message_media - Added
get_scope_policy - Added
get_unread_digest - Added
include_chats - Added
invite_member - Added
kick_member - Added
leave_group - Added
list_contacts - Added
list_conversations - Added
list_excluded_chats - Added
list_stickers - Added
login_complete - Added
mark_read - Added
preview_sticker - Added
react_message - Added
rename_group - Added
search_messages - Added
search_stickers - Added
send_audio - Added
send_contact - Added
send_file - Added
send_image - Added
send_location - Added
send_message - Added
send_sticker - Added
send_video - Added
unblock_contact - Added
unsend_message
38 tool updates
v0.3.0- Removed
accept_invitation - Removed
add_friend - Removed
cancel_reaction - Removed
collect_messages - Removed
create_group - Removed
exclude_chats - Removed
find_contact - Removed
get_chat_messages - Removed
get_group_members - Removed
get_message_image - Removed
get_message_media - Removed
get_scope_policy - Removed
get_unread_digest - Removed
include_chats - Removed
invite_member - Removed
kick_member - Removed
leave_group - Removed
list_contacts - Removed
list_conversations - Removed
list_excluded_chats - Removed
list_stickers - Removed
login_complete - Removed
mark_read - Removed
preview_sticker - Removed
react_message - Removed
rename_group - Removed
search_messages - Removed
search_stickers - Removed
send_audio - Removed
send_contact - Removed
send_file - Removed
send_image - Removed
send_location - Removed
send_message - Removed
send_sticker - Removed
send_video - Removed
unblock_contact - Removed
unsend_message
40 tool updates
v0.2.0- First observed
accept_invitation - First observed
add_friend - First observed
block_contact - First observed
cancel_reaction - First observed
collect_messages - First observed
create_group - First observed
exclude_chats - First observed
find_contact - First observed
get_chat_messages - First observed
get_group_members - First observed
get_message_image - First observed
get_message_media - First observed
get_scope_policy - First observed
get_unread_digest - First observed
include_chats - First observed
invite_member - First observed
kick_member - First observed
leave_group - First observed
list_contacts - First observed
list_conversations - First observed
list_excluded_chats - First observed
list_stickers - First observed
login - First observed
login_complete - First observed
mark_read - First observed
preview_sticker - First observed
react_message - First observed
rename_group - First observed
search_messages - First observed
search_stickers - First observed
send_audio - First observed
send_contact - First observed
send_file - First observed
send_image - First observed
send_location - First observed
send_message - First observed
send_sticker - First observed
send_video - First observed
unblock_contact - First observed
unsend_message
TDQS
Scored across 41 tools
Most tools target a distinct resource+action, and descriptions actively disambiguate the tricky clusters (get_unread_digest vs list_conversations vs get_insight; find_contact vs list_contacts; search_stickers vs list_stickers). The only real overlaps are the explicitly-flagged legacy alias get_message_image vs get_message_media and collect_messages vs search_messages' auto-collect, both of which are documented so an agent won't misselect.
Nearly every tool follows a clean snake_case verb_noun pattern (get_*, list_*, send_*, search_*, create_/rename_/invite_/kick_/leave_group, block_/unblock_contact). The send_* family, group-management family, and privacy family are each internally uniform. Only 'login' / 'login_complete' deviate slightly, which is negligible.
41 tools is heavy and sits well above the comfortable 3-15 range, largely because seven distinct send_* variants (message/image/file/audio/video/location/contact/sticker) plus a full login pair inflate the surface. Each tool is genuinely distinct and earns its place in a rich LINE domain, but some consolidation (e.g. a single send_media with a type param) would be reasonable.
The surface covers the full lifecycle: auth (login/login_complete), conversation listing/search/digest/insight, message fetch and media download, all outbound message types, group CRUD and membership, contact add/block/find, reactions/unsend, stickers, and a complete privacy denylist lifecycle (exclude/include/list/policy). No obvious dead ends for the stated purpose.
Maintenance
Related MCP Connectors
Read, search and reply to your WhatsApp chats from your AI, using your own number.
Email for AI agents: read, search and send from your Gmail, Outlook, Yahoo, iCloud or IMAP inbox.
WhatsApp for Claude, ChatGPT or any AI agent: search, reply and send. Personal or Business API.
Read-only WhatsApp MCP for your AI agent: it reads the chats you choose. Try the public demo.
Related MCP Servers
- AlicenseAqualityCmaintenanceOperate your LINE Official Account from any AI agent through natural language.37339 npm102MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI tools to read and send messages through LINE Desktop via MCP, supporting manual or automatic sending without official LINE API tokens.118 npm127MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI tools to read and send LINE messages by automating the LINE Desktop application, with macOS OCR-based reading and idle-aware scheduling.118 npm1MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI assistants to read, search, and reply to personal LINE messages, including end-to-end-encrypted chats and media, all decrypted locally.MIT