{"_id":"@rikaidev/yomi","_rev":"31-808d8e013385fa97ddebdbe6f9c9a0fc","name":"@rikaidev/yomi","dist-tags":{"latest":"0.5.0"},"versions":{"0.2.6":{"name":"@rikaidev/yomi","version":"0.2.6","keywords":["line","mcp","model-context-protocol","e2ee","messaging","chat"],"license":"MIT","_id":"@rikaidev/yomi@0.2.6","maintainers":[{"name":"gloomcheng","email":"gloomcheng@gmail.com"}],"homepage":"https://github.com/RikaiDev/yomi#readme","bugs":{"url":"https://github.com/RikaiDev/yomi/issues"},"bin":{"yomi":"run.mjs"},"dist":{"shasum":"efd7341c82dc8ca9024ba489a3f417c35ccb5acf","tarball":"https://registry.npmjs.org/@rikaidev/yomi/-/yomi-0.2.6.tgz","fileCount":119,"integrity":"sha512-rHm+y0qUjRgup1a+Vb2+pbDXgj1ZBuGw3ZKBevlWCnuTTPgMKh73TzNQVv9taekoc1RJyhAcjsKkbO7LUBhvNw==","signatures":[{"sig":"MEUCIGW71rzXs36xIdNmg3kARPF98cptWqJqDs8i3yttuHztAiEA4OVpmNjFZigxI4ftKTKwx8YR6kmWlIoT8ayO2Kmhq70=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@rikaidev%2fyomi@0.2.6","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":662241},"type":"module","engines":{"node":">=22.13.0 <23.0.0 || >=23.4.0"},"gitHead":"32a920a061346b48ba35d09ff98cbe0ae48ebad7","scripts":{"lint":"biome check src/","test":"bun test","build":"tsc","start":"node run.mjs","format":"biome format --write src/","version":"node scripts/sync-version.mjs && git add src/version.ts","lint:fix":"biome check --write src/","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:87c9c261-c822-4668-93d7-ef8da146f75d"}},"repository":{"url":"git+https://github.com/RikaiDev/yomi.git","type":"git"},"_npmVersion":"12.0.1","description":"Yomi (読み) — reads your LINE (decrypting Letter-Sealing E2EE) and sends on your behalf, via a stdio MCP server, so an agent can catch up on your LINE for you.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"tweetnacl":"^1.0.3","curve25519-js":"^0.0.4","@huggingface/transformers":"^4.2.0","@modelcontextprotocol/sdk":"^1.12.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"^1.3.14","typescript":"^7.0.0","@types/node":"^22.10.0","@biomejs/biome":"^2.5.3"},"_npmOperationalInternal":{"tmp":"tmp/yomi_0.2.6_1784219070380_0.15685716729869004","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@rikaidev/yomi","version":"0.3.0","keywords":["line","mcp","model-context-protocol","e2ee","messaging","chat"],"license":"MIT","_id":"@rikaidev/yomi@0.3.0","maintainers":[{"name":"gloomcheng","email":"gloomcheng@gmail.com"}],"homepage":"https://github.com/RikaiDev/yomi#readme","bugs":{"url":"https://github.com/RikaiDev/yomi/issues"},"bin":{"yomi":"run.mjs"},"dist":{"shasum":"3d05b5eb36ae902c1e923f883bf8bab9cc82629e","tarball":"https://registry.npmjs.org/@rikaidev/yomi/-/yomi-0.3.0.tgz","fileCount":120,"integrity":"sha512-LfERIts/cgW6vcnEQy+mp/OWv4VFFM4b+uEm8jUENQU1Zs2Hc2a7voacN4An5EB4BHNOdolD56CsCo07HFD3+Q==","signatures":[{"sig":"MEUCIQDWS6YBMcljFAzMmvDpiuECWV4Jw3RK5Xc5fwKoAne1vwIgbPPsNEMTPLt+2HX6NzPTYcEEXqllc42aar2gKodsNyU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@rikaidev%2fyomi@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":671896},"type":"module","engines":{"node":">=22.13.0 <23.0.0 || >=23.4.0"},"gitHead":"9e8313519416556c3607ff2346e261ddff22762e","scripts":{"lint":"biome check src/","test":"bun test","build":"tsc","start":"node run.mjs","format":"biome format --write src/","version":"node scripts/sync-version.mjs && git add src/version.ts","lint:fix":"biome check --write src/","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:87c9c261-c822-4668-93d7-ef8da146f75d"}},"repository":{"url":"git+https://github.com/RikaiDev/yomi.git","type":"git"},"_npmVersion":"12.0.1","description":"Yomi (読み) — reads your LINE (decrypting Letter-Sealing E2EE) and sends on your behalf, via a stdio MCP server, so an agent can catch up on your LINE for you.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"tweetnacl":"^1.0.3","curve25519-js":"^0.0.4","@toon-format/toon":"^2.3.1","@huggingface/transformers":"^4.2.0","@modelcontextprotocol/sdk":"^1.12.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"knip":"^6.27.0","@types/bun":"^1.3.14","typescript":"^7.0.0","@types/node":"^22.10.0","@biomejs/biome":"^2.5.3"},"_npmOperationalInternal":{"tmp":"tmp/yomi_0.3.0_1784363105529_0.02832861933784292","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@rikaidev/yomi","version":"0.3.1","keywords":["line","mcp","model-context-protocol","e2ee","messaging","chat"],"license":"MIT","_id":"@rikaidev/yomi@0.3.1","maintainers":[{"name":"gloomcheng","email":"gloomcheng@gmail.com"}],"homepage":"https://rikaidev.github.io/yomi/","bugs":{"url":"https://github.com/RikaiDev/yomi/issues"},"bin":{"yomi":"run.mjs"},"dist":{"shasum":"3891d8a8ea1016c15f3910b3f2aebe6b030d3053","tarball":"https://registry.npmjs.org/@rikaidev/yomi/-/yomi-0.3.1.tgz","fileCount":120,"integrity":"sha512-/Dl3psNgc8r88O9gGL410WXleNLUQv8bwnrlSqofWRH2fy6VVMpaF26j032UPa+wl3C/I25uWJnK9gF3FmApFQ==","signatures":[{"sig":"MEQCID/27xuJWBQ4kqP1sy4T7OCEKr/O0QE2NpAgSyLfrjIVAiByjRiTMN+KxVcZp8WYiXU83hNOAdV1wxpwnu8VLLTCFA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@rikaidev%2fyomi@0.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":674954},"type":"module","engines":{"node":">=22.13.0 <23.0.0 || >=23.4.0"},"gitHead":"fe6b03d6855bd83cf76a818bda22f6a6b39a0529","scripts":{"lint":"biome check src/","test":"bun test","build":"tsc","start":"node run.mjs","format":"biome format --write src/","version":"node scripts/sync-version.mjs && git add src/version.ts","lint:fix":"biome check --write src/","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:87c9c261-c822-4668-93d7-ef8da146f75d"}},"repository":{"url":"git+https://github.com/RikaiDev/yomi.git","type":"git"},"_npmVersion":"12.0.1","description":"Open-source personal LINE MCP server for AI agents. Read, reply, send images, and search LINE conversations locally with Letter-Sealing E2EE.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"tweetnacl":"^1.0.3","curve25519-js":"^0.0.4","@toon-format/toon":"^2.3.1","@huggingface/transformers":"^4.2.0","@modelcontextprotocol/sdk":"^1.12.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"knip":"^6.27.0","@types/bun":"^1.3.14","typescript":"^7.0.0","@types/node":"^22.10.0","@biomejs/biome":"^2.5.3"},"_npmOperationalInternal":{"tmp":"tmp/yomi_0.3.1_1784480283313_0.241866278333271","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@rikaidev/yomi","version":"0.4.0","keywords":["line","mcp","model-context-protocol","e2ee","messaging","chat"],"license":"MIT","_id":"@rikaidev/yomi@0.4.0","maintainers":[{"name":"gloomcheng","email":"gloomcheng@gmail.com"}],"homepage":"https://rikaidev.github.io/yomi/","bugs":{"url":"https://github.com/RikaiDev/yomi/issues"},"bin":{"yomi":"run.mjs"},"dist":{"shasum":"96f716aa7f692a7b2d9a2518eb0d5c131c917254","tarball":"https://registry.npmjs.org/@rikaidev/yomi/-/yomi-0.4.0.tgz","fileCount":120,"integrity":"sha512-txJAG783TgIanIJKzsDKoGIvOY0YUimaE3hr1QZ/ZESFuMea8zWxKkSaPjSK8vlG3dumSh0iGtY1B3/n1hfOQQ==","signatures":[{"sig":"MEQCIFDEZPp0ObXmTAow9XUR5nRSuNkAPKdjdTt5M83+3SEaAiAgTlQPQhaDEr4wkr2jLI7yjh3Tv5K7+XAornmHupatnQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@rikaidev%2fyomi@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":670015},"type":"module","engines":{"node":">=22.13.0 <23.0.0 || >=23.4.0"},"gitHead":"1fd6972986bf1ddebec0ad89348c3b58ee39722e","scripts":{"lint":"biome check src/","test":"bun test","build":"tsc","start":"node run.mjs","format":"biome format --write src/","version":"node scripts/sync-version.mjs && git add src/version.ts","lint:fix":"biome check --write src/","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:87c9c261-c822-4668-93d7-ef8da146f75d"}},"repository":{"url":"git+https://github.com/RikaiDev/yomi.git","type":"git"},"_npmVersion":"12.0.1","description":"Open-source personal LINE MCP server for AI agents. Read, reply, send images, and search LINE conversations locally with Letter-Sealing E2EE.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"tweetnacl":"^1.0.3","curve25519-js":"^0.0.4","@toon-format/toon":"^2.3.1","@huggingface/transformers":"^4.2.0","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"knip":"^6.27.0","@types/bun":"^1.3.14","typescript":"^7.0.0","@types/node":"^22.10.0","@biomejs/biome":"^2.5.3","@modelcontextprotocol/client":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/yomi_0.4.0_1785335301888_0.5788602048601199","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@rikaidev/yomi","version":"0.4.1","keywords":["line","mcp","model-context-protocol","e2ee","messaging","chat"],"license":"MIT","_id":"@rikaidev/yomi@0.4.1","maintainers":[{"name":"gloomcheng","email":"gloomcheng@gmail.com"}],"homepage":"https://rikaidev.github.io/yomi/","bugs":{"url":"https://github.com/RikaiDev/yomi/issues"},"bin":{"yomi":"run.mjs"},"dist":{"shasum":"a8e355196aaffe6dde794bbcec3ac34ccea9aa0e","tarball":"https://registry.npmjs.org/@rikaidev/yomi/-/yomi-0.4.1.tgz","fileCount":125,"integrity":"sha512-CpgHyYFtHGBPhG3pF8YaJo8i3yFQPzHu3KxmQo4zV/cKwCXWZdxTk1hA6ohrx/4/z439r7BlXzBa54Db2NZqHQ==","signatures":[{"sig":"MEQCIDdYP+w0N79hTpcYM7ey6DbHNwwgnEatkXB7AYFJOTynAiAWPV31e1g5B1pEbTQTOxbP5A5VusUYDWcWELUC02VwTw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@rikaidev%2fyomi@0.4.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":695796},"type":"module","engines":{"node":">=22.13.0 <23.0.0 || >=23.4.0"},"gitHead":"e92b32509e662962a3815715ec3469fdef41c15c","scripts":{"lint":"biome check src/","test":"bun test","build":"tsc","start":"node run.mjs","format":"biome format --write src/","version":"node scripts/sync-version.mjs && git add src/version.ts","lint:fix":"biome check --write src/","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:87c9c261-c822-4668-93d7-ef8da146f75d"}},"repository":{"url":"git+https://github.com/RikaiDev/yomi.git","type":"git"},"_npmVersion":"12.0.2","description":"Open-source personal LINE MCP server for AI agents. Read, reply, send images, and search LINE conversations locally with Letter-Sealing E2EE.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"tweetnacl":"^1.0.3","curve25519-js":"^0.0.4","@toon-format/toon":"^2.3.1","@huggingface/transformers":"^4.2.0","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"knip":"^6.27.0","@types/bun":"^1.3.14","typescript":"^7.0.0","@types/node":"^22.10.0","@biomejs/biome":"^2.5.3","@modelcontextprotocol/client":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/yomi_0.4.1_1785429250875_0.7144099571068163","host":"s3://npm-registry-packages-npm-production"}},"0.4.2":{"name":"@rikaidev/yomi","version":"0.4.2","keywords":["line","mcp","model-context-protocol","e2ee","messaging","chat"],"license":"MIT","_id":"@rikaidev/yomi@0.4.2","maintainers":[{"name":"gloomcheng","email":"gloomcheng@gmail.com"}],"homepage":"https://rikaidev.github.io/yomi/","bugs":{"url":"https://github.com/RikaiDev/yomi/issues"},"bin":{"yomi":"run.mjs"},"dist":{"shasum":"91838e9a6905cac93cbe6dec122965ad96e3e9a8","tarball":"https://registry.npmjs.org/@rikaidev/yomi/-/yomi-0.4.2.tgz","fileCount":125,"integrity":"sha512-OQTiO3kNXSSnxi53UN4OWeyqxZmx0JYB9hSLQeNugpsSzQWIMFnDH4Zn+ekZsTdIdwL5rp3yFksNMqmINuSYJA==","signatures":[{"sig":"MEUCIQC83x94cTyvaR+OEtjMXqoJpTcNZI3n5fzL7vXIQJ1GLgIgC8f05eEomjmsxx4fPbdo7bMm6D8KkWe3i1X9kI9+OS8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@rikaidev%2fyomi@0.4.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":696005},"type":"module","engines":{"node":">=22.13.0 <23.0.0 || >=23.4.0"},"gitHead":"0b91323d2656c534382bbf43fd4cdc93e4879afd","scripts":{"lint":"biome check src/","test":"bun test","build":"tsc","start":"node run.mjs","format":"biome format --write src/","version":"node scripts/sync-version.mjs && git add src/version.ts","lint:fix":"biome check --write src/","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:87c9c261-c822-4668-93d7-ef8da146f75d"}},"repository":{"url":"git+https://github.com/RikaiDev/yomi.git","type":"git"},"_npmVersion":"12.0.2","description":"Open-source personal LINE MCP server for AI agents. Read, reply, send images, and search LINE conversations locally with Letter-Sealing E2EE.","directories":{},"_nodeVersion":"24.18.0","dependencies":{"tweetnacl":"^1.0.3","curve25519-js":"^0.0.4","@toon-format/toon":"^2.3.1","@huggingface/transformers":"^4.2.0","@modelcontextprotocol/server":"^2.0.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"knip":"^6.27.0","@types/bun":"^1.3.14","typescript":"^7.0.0","@types/node":"^22.10.0","@biomejs/biome":"^2.5.3","@modelcontextprotocol/client":"^2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/yomi_0.4.2_1785762182198_0.8503055873380625","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@rikaidev/yomi","engines":{"node":">=22.13.0 <23.0.0 || >=23.4.0"},"version":"0.5.0","description":"Open-source personal LINE MCP server for AI agents. Read, reply, send images, and search LINE conversations locally with Letter-Sealing E2EE.","type":"module","license":"MIT","homepage":"https://rikaidev.github.io/yomi/","repository":{"type":"git","url":"git+https://github.com/RikaiDev/yomi.git"},"bugs":{"url":"https://github.com/RikaiDev/yomi/issues"},"keywords":["line","mcp","model-context-protocol","e2ee","messaging","chat"],"bin":{"yomi":"run.mjs"},"publishConfig":{"access":"public"},"scripts":{"build":"tsc","typecheck":"tsc --noEmit","start":"node run.mjs","lint":"biome check src/","lint:fix":"biome check --write src/","lint:comments":"node scripts/lint-comments.mjs","hooks:install":"git config core.hooksPath .githooks","pretest":"npm run lint:comments","format":"biome format --write src/","test":"bun test","version":"node scripts/sync-version.mjs && git add src/version.ts","prepublishOnly":"npm run build"},"dependencies":{"@huggingface/transformers":"^4.2.0","@modelcontextprotocol/server":"^2.0.0","@toon-format/toon":"^2.3.1","curve25519-js":"^0.0.4","tweetnacl":"^1.0.3"},"devDependencies":{"@biomejs/biome":"^2.5.3","@modelcontextprotocol/client":"^2.0.0","@types/bun":"^1.3.14","@types/node":"^22.10.0","knip":"^6.27.0","typescript":"^7.0.0"},"gitHead":"2e91a87b15786706868987cec720c61cc67a0c98","_id":"@rikaidev/yomi@0.5.0","_nodeVersion":"24.19.0","_npmVersion":"12.0.2","dist":{"integrity":"sha512-RUpDmj3tmMABZwxnW7SruWkZ53i3fGuu+OVFHV/dS0nXFyDJtETHjfNuMqgX2aW78kXeHCmJeNsAsMG95/Ykkw==","shasum":"cd0ddc1f117067ef98c35082745451f1fadc654c","tarball":"https://registry.npmjs.org/@rikaidev/yomi/-/yomi-0.5.0.tgz","fileCount":131,"unpackedSize":757298,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@rikaidev%2fyomi@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFev08nBI3o4DmXFbnOJ/qkdOW1+OjNqpTxVtGBSnes6AiEA+q33yytkM6jevcGCEmII5uVPbDF3pVPllFrSQsnbyos="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:87c9c261-c822-4668-93d7-ef8da146f75d"}},"directories":{},"maintainers":[{"name":"gloomcheng","email":"gloomcheng@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/yomi_0.5.0_1787529308384_0.3093634523746931"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-10T17:36:13.648Z","modified":"2026-08-23T23:55:08.881Z","0.1.0":"2026-07-10T17:36:13.999Z","0.1.1":"2026-07-12T07:04:28.932Z","0.1.2":"2026-07-13T09:00:36.497Z","0.1.3":"2026-07-13T16:30:56.591Z","0.1.4":"2026-07-13T17:05:10.044Z","0.2.0":"2026-07-15T19:07:21.021Z","0.2.1":"2026-07-16T10:00:37.429Z","0.2.2":"2026-07-16T11:05:12.318Z","0.2.3":"2026-07-16T14:20:51.035Z","0.2.4":"2026-07-16T15:18:37.749Z","0.2.5":"2026-07-16T16:12:54.604Z","0.2.6":"2026-07-16T16:24:30.550Z","0.3.0":"2026-07-18T08:25:05.683Z","0.3.1":"2026-07-19T16:58:03.482Z","0.4.0":"2026-07-29T14:28:22.050Z","0.4.1":"2026-07-30T16:34:11.021Z","0.4.2":"2026-08-03T13:03:02.364Z","0.5.0":"2026-08-23T23:55:08.528Z"},"bugs":{"url":"https://github.com/RikaiDev/yomi/issues"},"license":"MIT","homepage":"https://rikaidev.github.io/yomi/","keywords":["line","mcp","model-context-protocol","e2ee","messaging","chat"],"repository":{"type":"git","url":"git+https://github.com/RikaiDev/yomi.git"},"description":"Open-source personal LINE MCP server for AI agents. Read, reply, send images, and search LINE conversations locally with Letter-Sealing E2EE.","maintainers":[{"name":"gloomcheng","email":"gloomcheng@gmail.com"}],"readme":"<!-- rikai-logo -->\n<p align=\"center\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\".github/assets/logo-dark.svg\">\n    <img src=\".github/assets/logo.svg\" alt=\"yomi\" width=\"96\" height=\"96\">\n  </picture>\n</p>\n\n# Yomi (読み) — the personal LINE MCP server\n\n**Yomi is an open-source LINE MCP server for your personal account. Read, reply,\nsend images, and search every conversation from Claude or any local AI agent —\nwithout a browser, bot account, or LINE's own client.**\n\n*The name reads more than one way. **読み** (yomi) — a reading: not just parsing\nyour messages, but reading the situation, the way you do. **詠み** (yomi) — to\nrecite: it doesn't read in silence, it reads things back to you. And **黄泉**\n(yomi) — the unreachable realm; the very yomi in **黄泉帰り** (*yomigaeri*),\n\"returning from Yomi.\" Like a messenger moving between two realms, Yomi passes\nthrough sealing and silence to bring messages still readable deep inside your\nconversations back into the light. It does not summon history already lost; it\nhelps what has not disappeared be seen again.*\n\nUnlike LINE's official Bot MCP server, which connects AI agents to a LINE\nOfficial Account through the Messaging API, Yomi connects to your existing\npersonal LINE account and conversations as a secondary device.\n\nYomi speaks LINE's TCompact-over-HTTPS protocol directly, decrypts Letter-Sealing\n(E2EE) messages and media, and exposes the result to any AI agent through a small\nstdio [MCP](https://modelcontextprotocol.io) server. Point Claude Desktop (or any\nMCP client) at it and the agent can catch up on your LINE the way you do — read,\nreply, send an image, mention someone, and search your whole history locally — and,\nwith `get_insight`, read the *situation*: which conversations are waiting on your\nreply, who ties your world together, and what has gone overdue.\n\nNo official API, no bot account, no webhook. Yomi logs in as a secondary device\non your own account.\n\nGuides: **[What is a LINE MCP server?](https://rikaidev.github.io/yomi/line-mcp/)**\n· **[LINE MCP 是什麼？Yomi、官方 Bot 與桌面自動化的差異](https://rikaidev.github.io/yomi/zh-tw/line-mcp/)**.\n\n![license](https://img.shields.io/badge/license-MIT-blue) ![runtime](https://img.shields.io/badge/runtime-node%20%7C%20bun-black) ![protocol](https://img.shields.io/badge/MCP-stdio-green) ![npm](https://img.shields.io/badge/npm-@rikaidev%2Fyomi-red)\n\n> **Unofficial.** Yomi is an independent personal project, not affiliated with or\n> endorsed by LINE. Using it may be against LINE's Terms of Service, and running an\n> additional client on your account carries a risk of rate-limiting or suspension.\n> It is intended for reading *your own* account. Use it at your own risk. See\n> [Disclaimer](#disclaimer).\n\n---\n\n## Getting started\n\nYou need [Node.js](https://nodejs.org) and a LINE account. Yomi runs locally\nthrough `npx`; you do not need to clone this repository, install Bun, or build\nanything. Run `node --version` first, and install the current LTS if the command\nis missing or the version is unsupported.\n\nYomi's local search index uses Node's built-in `node:sqlite`, so it needs\n**v22.13 or newer — except v23.0–v23.3**, which are newer than v22.13 yet still\nlack that module ([it was unflagged in both 22.13.0 and\n23.4.0](https://github.com/nodejs/node/pull/55890)). Any current LTS is fine.\nThis is `engines.node` in `package.json`; the rest of Yomi runs on older Node,\nbut search, scope and capture do not.\n\n> **⚠️ Yomi needs a client that runs it on your own machine.**\n> Cloud-only tools (ChatGPT, Claude.ai web) cannot run Yomi. Configure Yomi in\n> Claude Desktop or Claude Code and it works in both chat and Cowork, because\n> Desktop starts Yomi on your machine and Cowork's local sessions load it. Do not\n> ask Cowork to *install* Yomi for you: Cowork's shell runs inside a throwaway VM,\n> not on your machine, so anything it installs there is gone when the session ends.\n> Follow the steps below yourself, in your own terminal.\n\nChoose the client you actually use and follow only that section. Claude Code and\nClaude Desktop have separate MCP settings; configuring one does not configure the\nother.\n\n<details open>\n<summary><strong>Claude Desktop — one-click install (no terminal, no Node)</strong></summary>\n\nThe easiest path, and the only one that needs no command line at all. Claude\nDesktop ships its own Node runtime, so nothing else has to be installed.\n\n1. Download the bundle for your machine from the\n   [latest release](https://github.com/RikaiDev/yomi/releases/latest):\n\n   | Machine | File |\n   | --- | --- |\n   | Windows (Intel/AMD) | `yomi-win32-x64.mcpb` |\n   | Mac (Apple Silicon) | `yomi-darwin-arm64.mcpb` |\n   | Linux (x64) | `yomi-linux-x64.mcpb` |\n\n2. In Claude Desktop, open **Settings → Extensions**, and drag the downloaded\n   file onto that page (or just double-click the file).\n\n3. Review what it asks for, click **Install**.\n\n4. Start a conversation and say *\"log in to LINE\"*. Yomi shows a form, you enter\n   your phone number, and you confirm on your phone. No terminal at any point.\n\nThis skips the config file entirely, which also sidesteps the Windows MSIX bug\ndescribed below.\n\n> **Note for Cowork users:** do not ask Cowork to install Yomi for you. Cowork's\n> shell runs inside a throwaway VM — not on your machine — and it will not type\n> into your real terminal. Install the bundle yourself with the three clicks\n> above; once installed, Cowork's local sessions can use Yomi like any other tool.\n\n</details>\n\n<details>\n<summary><strong>Claude Desktop — manual config (npx)</strong></summary>\n\n1. Find the full path to `npx`:\n\n   ```text\n   macOS / Linux: which npx\n   Windows:       where npx\n   ```\n\n2. Open the Claude Desktop config file:\n\n   ```text\n   macOS:   ~/Library/Application Support/Claude/claude_desktop_config.json\n   Windows: %APPDATA%\\Claude\\claude_desktop_config.json\n   ```\n\n   > **⚠️ Windows: the documented path may not be the one Claude actually reads.**\n   > Claude Desktop ships as an MSIX package, whose filesystem is virtualized. The\n   > app reads its config from\n   >\n   > ```text\n   > %LOCALAPPDATA%\\Packages\\Claude_pzs8sxrjxfjjc\\LocalCache\\Roaming\\Claude\\claude_desktop_config.json\n   > ```\n   >\n   > while **Settings → Developer → Edit Config** opens the *un*virtualized\n   > `%APPDATA%\\Claude\\` file. They are two different files that never sync, so a\n   > correct Yomi config written to the documented path is **silently ignored** — no\n   > error, no log, Yomi simply never appears. This is\n   > [claude-code#26073](https://github.com/anthropics/claude-code/issues/26073),\n   > still open. If Yomi does not show up after a restart, write the same config to\n   > the `LocalCache` path above as well. (Not applicable to Claude Code, or to\n   > Desktop installed outside MSIX.)\n\n3. Add Yomi under `mcpServers`, replacing the example `command` with the full\n   path printed in step 1. On Windows, JSON requires each `\\` in the path to be\n   written as `\\\\`:\n\n```json\n{\n  \"mcpServers\": {\n    \"yomi\": {\n      \"command\": \"/opt/homebrew/bin/npx\",\n      \"args\": [\"@rikaidev/yomi\"]\n    }\n  }\n}\n```\n\n   For example, a Windows path may look like\n   `\"C:\\\\Program Files\\\\nodejs\\\\npx.cmd\"`. Use the path reported on your own\n   machine rather than copying either example blindly.\n\n   > **⚠️ The `npx` you name here does not decide which Node runs Yomi.**\n   > On macOS and Linux `npx` is a script beginning with `#!/usr/bin/env node`,\n   > so it runs whichever `node` comes first on **Claude Desktop's** `PATH` —\n   > not the Node installed beside the `npx` you just pointed at. Desktop\n   > inherits its `PATH` from the desktop session, which is often not your\n   > terminal's, and version managers (nvm, fnm, asdf, volta, …) put their own\n   > `node` ahead of everything else. So `which npx` can report a perfectly\n   > modern install while Yomi still starts on an old Node.\n   >\n   > The symptom is specific: logging in, reading and sending all work, but\n   > search, scope and capture fail, and the log says\n   > `No such built-in module: node:sqlite`. Yomi prints the runtime it actually\n   > got in that error — read it rather than assuming.\n   >\n   > The fix is to give the server a `PATH` whose first entry holds a supported\n   > `node`:\n   >\n   > ```json\n   > \"yomi\": {\n   >   \"command\": \"<the npx path from step 1>\",\n   >   \"args\": [\"-y\", \"@rikaidev/yomi\"],\n   >   \"env\": { \"PATH\": \"<dir holding a supported node><sep><a base PATH>\" }\n   > }\n   > ```\n   >\n   > Separator: `:` on macOS/Linux, `;` on Windows. Keep a usable base after it\n   > (`/usr/local/bin:/usr/bin:/bin` or your platform's equivalent) — this\n   > replaces the server's `PATH` rather than prepending to it, and `npx` needs\n   > the ordinary tools.\n   >\n   > Naming an absolute `node` in `command` does **not** work, however plausible\n   > it looks: `npx` spawns the package's own bin as a child process and resolves\n   > `node` from `PATH` again for it, so `command` only picks who runs `npx`, not\n   > who runs Yomi. Setting `PATH` is what actually reaches the server.\n\n**Configuring this with an AI agent?** MCP configs are mostly written by agents\nnow rather than by hand, so the steps above are stated for a human reading their\nown machine. An agent should resolve the values instead of emitting placeholders,\nand this setup has one trap worth stating outright: naming `npx` in `command`\ndoes **not** pin the runtime. Procedure:\n\n1. Find a Node that actually has the module Yomi's index needs — do not infer it\n   from a version number:\n\n   ```bash\n   node -e 'require(\"node:sqlite\")' && command -v node    # POSIX\n   node -e \"require('node:sqlite')\" && where node         # Windows\n   ```\n\n   If it throws, that Node is unusable for search/scope/capture. Try the other\n   installs (`which -a node`, `nvm ls`, `fnm list`, …) until one passes; only\n   then continue.\n\n2. Write that Node's **directory** into `env.PATH`, per the JSON above, and keep\n   a base `PATH` after it. Resolve the directory on the target machine\n   (`dirname` of the path from step 1); do not copy an example out of this\n   README — they are Apple Silicon Homebrew paths and wrong nearly everywhere.\n\n   Do not instead put an absolute `node` in `command`. It reads like the\n   stronger fix and is not one: `npx` spawns the package's bin as a child and\n   re-resolves `node` from `PATH` for it, so `command` only decides who runs\n   `npx`. This was measured, not assumed — with a Node 20 first on `PATH`,\n   `command`-pinning still gave the server Node 20.\n\n   `command -v` may also hand back a wrapper rather than a real binary — heap\n   shims, local-first `npx` wrappers and version-manager stubs are all common,\n   and some behave differently depending on the client's working directory. Do\n   not try to reason your way to the \"real\" one. Step 3 settles it.\n\n3. Verify with a tool that touches the index. **`yomi version` is not a\n   verification** — it prints a string without opening SQLite, so it succeeds on\n   a Node that cannot run search. Call `get_scope_policy`: it needs SQLite and no\n   LINE login. If it returns the policy text, the runtime is right. If it fails,\n   the error names the Node that actually ran and what it needed.\n\n4. Fully quit and reopen Claude Desktop. Confirm Yomi is loaded under **Settings →\n   Developer** before anything else: if Yomi is not listed there, Claude never read\n   your config — on Windows, see the MSIX warning in step 2. Once it is listed, the\n   tools are available in chat and in Cowork's local sessions alike.\n\nYou do **not** need to install Claude Code for this setup.\n\n</details>\n\n<details>\n<summary><strong>Claude Code</strong></summary>\n\n```bash\nclaude mcp add yomi -- npx @rikaidev/yomi\n```\n\nStart a new `claude` session. The Yomi tools should appear automatically. This\ncommand configures Claude Code only; it does not configure Claude Desktop.\n\nThis form leaves the runtime to `PATH`, which is fine as long as the `node` your\n`claude` session resolves is a supported one — it is the same `PATH` you can see,\nunlike Claude Desktop's. Check with `node -e 'require(\"node:sqlite\")'`. If it\nthrows, or you would rather not depend on `PATH` at all, name the Node\nexplicitly:\n\n```bash\nclaude mcp add yomi -e PATH=\"$(dirname \"$(command -v node)\"):$PATH\" -- npx -y @rikaidev/yomi\n```\n\nEither way, verify with `get_scope_policy` rather than `yomi version`: the\nversion command prints a string without opening SQLite, so it passes on a Node\nthat cannot run search.\n\n> **Running Claude Code inside a clone of this repo?** Spell the spec\n> `@rikaidev/yomi@latest`. Without a tag, `npx` looks for a local bin first, and\n> in this repo `package.json` declares `\"bin\": {\"yomi\": ...}` while nothing links\n> it into `node_modules/.bin` — so `npx` skips the install and dies with\n> `sh: yomi: command not found`. An explicit tag makes it fetch the published\n> package. Only affects working copies of Yomi itself; everywhere else the\n> untagged form is fine.\n\n</details>\n\n<details>\n<summary><strong>Other MCP clients</strong></summary>\n\nThis standard config works in most MCP clients:\n\n```json\n{\n  \"mcpServers\": {\n    \"yomi\": {\n      \"command\": \"npx\",\n      \"args\": [\"@rikaidev/yomi\"]\n    }\n  }\n}\n```\n\n<details>\n<summary>Cursor</summary>\n\n`Cursor Settings` → `MCP` → `Add new MCP Server` → name it `yomi`, command type, value: `npx @rikaidev/yomi`\n\nOr add to `.cursor/mcp.json` in your project root.\n</details>\n\n<details>\n<summary>VS Code (GitHub Copilot)</summary>\n\nAdd to `.vscode/mcp.json`:\n\n```json\n{\n  \"servers\": {\n    \"yomi\": {\n      \"command\": \"npx\",\n      \"args\": [\"@rikaidev/yomi\"]\n    }\n  }\n}\n```\n</details>\n\n<details>\n<summary>opencode</summary>\n\nAdd to `~/.config/opencode/opencode.json`:\n\n```json\n{\n  \"mcp\": {\n    \"yomi\": {\n      \"type\": \"local\",\n      \"command\": [\"npx\", \"@rikaidev/yomi\"],\n      \"enabled\": true\n    }\n  }\n}\n```\n</details>\n\n<details>\n<summary>Codex</summary>\n\n```bash\ncodex mcp add yomi npx @rikaidev/yomi\n```\n\nOr add to `~/.codex/config.toml`:\n\n```toml\n[mcp_servers.yomi]\ncommand = \"npx\"\nargs = [\"@rikaidev/yomi\"]\n```\n</details>\n\n<details>\n<summary>Cline</summary>\n\nAdd to `cline_mcp_settings.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"yomi\": {\n      \"type\": \"stdio\",\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@rikaidev/yomi\"]\n    }\n  }\n}\n```\n</details>\n\n<details>\n<summary>Windsurf</summary>\n\nAdd to `~/.codeium/windsurf/mcp_config.json` — same JSON as standard config.\n</details>\n\n<details>\n<summary>Amp</summary>\n\n```bash\namp mcp add yomi -- npx @rikaidev/yomi\n```\n</details>\n\n<details>\n<summary>Goose</summary>\n\n`Advanced settings` → `Extensions` → `Add custom extension` → name `yomi`, type `STDIO`, command: `npx @rikaidev/yomi`\n</details>\n\n<details>\n<summary>Grok</summary>\n\n```bash\ngrok mcp add yomi -- npx @rikaidev/yomi\n```\n</details>\n\n</details>\n\n### First login\n\nOnce connected, tell the agent:\n\n> *\"Log into LINE — my number is +8869XXXXXXXX.\"*\n\nApprove the device on your phone (see [Logging in](#logging-in)), and:\n\n> *\"Summarize my unread LINE and tell me who's waiting on a reply.\"*\n\n---\n\n## What you can do\n\nYomi exposes **20 tools** over MCP (the ones worth naming; see the tables). Large\nread results use token-efficient [TOON](https://github.com/toon-format/toon);\nsmall status and write results use compact JSON. Errors remain plain text and media\nuses native MCP image/audio/resource content.\n\"Honest error\" below means an explicit failure naming the problem (e.g.\n`missing_decrypt_material`) — Yomi never fabricates a fallback, a placeholder, or a\nfake success.\n\n### Reading\n\n| Tool | Does |\n| --- | --- |\n| `get_unread_digest` | 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. |\n| `list_conversations` | Chats/groups/rooms with unread counts and a decrypted last-message preview. Newest-active first. |\n| `get_chat_messages` | One conversation, decrypted. Paginate deeper with a `before` cursor. Each message carries any raw `MENTION` metadata so you can see who was @-mentioned (a literal `@name` in the text is *not* a mention). |\n| `get_message_media` / `get_message_image` | Any decrypted attachment (image/video/audio/file). Honest error on non-media. |\n| `find_contact` / `list_contacts` | Friend-list lookup by name substring, or the full list. Raw LINE data — no fuzzy scoring, no affinity ranking. |\n| `get_group_members` | Members of a persistent group. Ad-hoc rooms without a group record fail honestly rather than returning a fake empty list. |\n\n### Insight (read the situation, not just the messages)\n\n| Tool | Does |\n| --- | --- |\n| `get_insight` | A compact *context network* over your local index — structure the agent reasons over, not a message dump. Returns `connectors` (people who appear across ≥2 of your chats, with the structural **bridges** whose removal would split your contact graph), `relationships` (per-conversation engagement and your typical reply **rhythm** there), and `open` (conversations whose latest message isn't yours, ranked by how **overdue** they are *relative to that rhythm*, each with a preview). It computes only structure and statistics: it deliberately does **not** decide who a message is addressed to, whether it's an open request or a closing acknowledgement, or a nickname's real identity — that is language the agent reads from each preview, dereferencing `get_chat_messages` only when a thread is worth judging. Encoded as [TOON](https://github.com/toon-format/toon), ≈¼ the tokens of the equivalent JSON. |\n\n### Writing (these really send — not drafts)\n\n| Tool | Does |\n| --- | --- |\n| `send_message` | Sends an E2EE text message now (pairwise key for 1:1, group key for groups/rooms). Optional `mentions` attach real @-mentions that LINE highlights and notifies; omit them and a literal `@name` is just text that notifies no one. One send per call, no retries. |\n| `send_image` | 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. |\n| `mark_read` | Sends a read receipt the other party can see. Explicit only — reading messages and background capture never mark anything read. |\n\n### Search (local, cross-conversation — LINE has no such primitive)\n\n| Tool | Does |\n| --- | --- |\n| `search_messages` | 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 `mode` (`hybrid`/`semantic`/`keyword`) so nothing is hidden. |\n| `collect_messages` | 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. |\n\n### Scope & privacy (all work offline, no LINE session needed)\n\n| Tool | Does |\n| --- | --- |\n| `exclude_chats` | 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. |\n| `include_chats` | Re-allow a conversation (does not restore purged data). |\n| `list_excluded_chats` / `get_scope_policy` | Show the denylist / the full privacy policy (read from [`PRIVACY.md`](PRIVACY.md)). |\n\n### Session\n\n| Tool | Does |\n| --- | --- |\n| `login` / `login_complete` | Passwordless secondary-device login. See below. `login` is the only tool callable without an existing session. |\n\n---\n\n## Logging in\n\n**Prerequisite:** on your primary phone, enable **設定 › 我的帳號 › 允許自其他裝置登入**\n(*Settings › Account › Allow login on other devices*). Without it LINE never offers\nthis device a sign-in prompt — this is the single most common reason a first login\nappears to hang.\n\n> **⚠️ Single Desktop Session Limit:** LINE allows only **one desktop client session at a time**\n> per account. Because Yomi connects as a desktop secondary device (`DESKTOPMAC`), logging into Yomi\n> will sign out your official LINE Desktop app (and signing back into LINE Desktop will invalidate\n> Yomi's session). You cannot use Yomi and the official LINE Desktop client simultaneously.\n\nYou only need to give the agent your phone number in E.164 form — it supplies the\nregion itself (e.g. `TW` for a `+886` number) when it calls the tool.\n\nYomi drives LINE's **passwordless (secondary-device)** flow. How the login surfaces\ndepends on your MCP client:\n\n- **Clients with the MCP `elicitation` capability** — `login` prompts you for your\n  phone/region, shows the PIN in a dialog, and blocks to completion. One call, no\n  PIN relayed through the model. Your phone number never enters the transcript.\n\n- **Clients without it (e.g. Claude Desktop today)** — two calls. `login` (with your\n  phone + region) returns the PIN in its result; enter it in LINE on your primary\n  phone and approve the device; then call `login_complete`, which blocks until the\n  phone confirms. The agent should call `login_complete` immediately — it does the\n  waiting, so there is nothing for you to report back.\n\n- **From a terminal** — `npx @rikaidev/yomi login` runs the whole flow on stdout, PIN and\n  all. Always available as a fallback.\n\n**The one deadline that matters is LINE's:** you have about **3 minutes** from when\nthe PIN is shown to enter it and approve the device. Yomi's own client keeps\nlistening for many minutes beyond that, so a slow phone is never the failure — only\nLINE's 3-minute code lifetime is.\n\nOnce you've logged in, the session — including a login *certificate* — is persisted\n(see [below](#sessions-and-credentials)), and future logins **skip the PIN\nentirely**.\n\n> There is also an experimental [MCP Apps](https://modelcontextprotocol.io) UI (a\n> `ui://yomi/login` card) for clients that render interactive views. It is\n> spec-correct and renders under the MCP Inspector, but some hosts fetch the resource\n> without completing the view handshake, so it is **display-only and never on the\n> critical path** — the flows above always work regardless.\n\n### Sessions and credentials\n\nYomi owns its own login. On startup it calls `resumeSession()` once, reading the\nLINE session from the macOS Keychain (service `dev.rikai.yomi.credentials`, account\n`line`) and silently refreshing the token if needed.\n\n- **First-party credentials.** The passwordless login persists the auth token,\n  refresh token, certificate, MID, and the E2EE keypair itself — then reads them\n  back to verify the write actually landed. A login that can't be persisted fails\n  loudly at login, not silently at the next restart.\n- **Shared session.** The session is stored in the canonical `dev.rikai.yomi.credentials`\n  keychain entry (or local credential store), allowing Yomi MCP and Yomi Desktop to\n  share the exact same LINE login session seamlessly.\n- **Platform note.** On macOS the session lives in the login Keychain. On **Linux\n  and Windows** Yomi currently falls back to a local JSON file — functional, but\n  less protected than an OS secret store, and less exercised than the macOS path.\n  Native secure-storage backends (libsecret / DPAPI) are planned; until then, treat\n  a non-macOS install accordingly.\n\nEvery tool except `login` and the offline scope/search tools returns an honest error\nwhen there is no session. Yomi is otherwise a pure query server — it never polls,\nnever backfills in the background; each tool call makes exactly the LINE requests it\nneeds, and `collect_messages` is the only path that fetches across many chats at once.\n\n---\n\n## Search\n\nLINE has no cross-conversation search; Yomi builds one locally. The index is a\ngitignored SQLite database (`data/search-index.db`) in the repo — **nothing leaves\nyour machine**.\n\nSearch is **hybrid** by design. It always runs FTS5 keyword search (bm25 over\nbigram-preprocessed text, so CJK substrings with no word boundaries are covered) and,\nwhen embeddings exist, semantic search, then fuses the two ranked lists by\n**Reciprocal Rank Fusion**. Pure semantic search silently drops exact matches that\nlive in un-embedded messages; pure keyword misses paraphrases; fusing both surfaces\nan exact term and a meaning-match together. The response's `mode` field always\nreports which methods contributed.\n\nSemantic ranking keeps each message as its own vector. Testing against real indexed\nLINE conversations found that embedding overlapping conversation windows diluted the\ncenter message and reduced retrieval quality. Context is therefore added only after\nranking: Yomi limits one chat from flooding the top results and expands each winner\nwith two messages on either side so the agent sees the exchange that gave the hit\nmeaning without weakening the embedding itself.\n\nSemantic ranking uses **`Xenova/bge-small-zh-v1.5`** (BAAI general embedding, small,\nChinese-primary but multilingual) via **transformers.js**. Embedding **inference runs\nfully in-process on CPU — your message text is never sent anywhere.**\n\nThe one caveat is a **one-time model download**: on the first `collect_messages` or\n`search_messages`, transformers.js fetches the model (~90 MB) from **`huggingface.co`**\nand caches it locally; every run afterwards is fully offline. That first fetch is an\noutbound HTTPS request to HuggingFace for the model weights — it carries your IP and\nwhich model is being downloaded, **but no message content and no LINE data**. If you\nneed Yomi fully air-gapped, pre-populate the transformers.js cache (or point it at a\nlocal model directory) before the first search so no network call is ever made.\n\n---\n\n## The secondary-device reality (read before trusting \"I can't get old messages\")\n\nYomi runs as a **secondary device** on your account. That shapes what it can see:\n\n- **Group chats *without* Letter Sealing are plaintext at the LINE server** — full\n  history and media, no restriction.\n- **Group chats *with* Letter Sealing are E2EE, and there the epoch matters.** Such a\n  group is encrypted with a shared *group key* that rotates (on membership changes, or\n  when a client provisions a new one). LINE only ever hands a device the **current**\n  group key — there is no API to fetch a superseded one. So a secondary device decrypts\n  messages from the epoch whose key it holds onward; messages sent under an **older\n  epoch — before Yomi obtained the current key — can come back undecryptable**, even in\n  a group whose earlier history it *can* read. This is inherent to Letter Sealing's\n  per-epoch group keys, not a bug here. It can also cut sideways: the epoch your phone\n  holds and the epoch Yomi holds need not be the same, so the **two devices can each\n  read a different slice** of the very same group.\n- **Installing Yomi does not itself rotate anything, so \"before I installed Yomi\" is\n  the wrong boundary.** Yomi only ever *resolves* an existing group key; it never\n  registers (mints) one, because minting rotates the group's shared secret for every\n  member and strands every message encrypted under the previous one. So a fresh install\n  picks up the **current** key and reads back to the last rotation — which may predate\n  the install by months. The boundary is the last rekey, not your install date. And\n  once Yomi has seen an epoch it keeps that key, so a rotation *after* install leaves\n  it able to read both sides. If a group comes back undecryptable all the way to\n  **today**, that is not this limitation — please report it.\n- **1:1 media** uses the account-level E2EE keychain, which a secondary device fully\n  possesses (LINE syncs it during pairing) — Yomi **can** decrypt 1:1 images/files it\n  can see.\n- **1:1 history *backfill* is the one real limitation.** LINE does not hand a\n  secondary device the *past* 1:1 message history the way it does for groups. Messages\n  received while Yomi is connected decrypt normally; deep-scrolling into 1:1 history\n  that predates pairing may come back empty. This is a LINE server-side restriction,\n  not a bug here.\n\nWhen decryption genuinely fails, Yomi returns an explicit `missing_decrypt_material`\nerror — never a fake card or placeholder. Silence is honest; a fabricated result is not.\n\n---\n\n## Development\n\nFor contributors working on the Yomi source code (requires [bun](https://bun.sh)\nor Node.js 24+ — `.nvmrc` pins the same 24 that CI installs, so `nvm use` in this\ndirectory picks it up; note nvm does not read `engines`, which is why the two are\nstated separately and guarded by a test):\n\n```bash\nbun install                 # install dependencies (or npm install)\nbun run.mjs                 # run the stdio MCP server\nbun run.mjs login           # run the login flow in a terminal\nnpm run build               # tsc --noEmit — type-check only (Yomi ships & runs from src/)\nnpm test                    # bun test\n```\n\nThe build only type-checks and compiles; this repo does not talk to live LINE\nservers as part of its own build. Connecting an MCP client and calling `login` is\nwhat actually starts a session.\n\n```\nsrc/\n  line/     LINE protocol core: TCompact/Thrift codec, E2EE (Letter-Sealing,\n            group keys, media), Talk/Auth/Sync service clients, session state,\n            passwordless login flow.\n  auth/     Credential store (macOS Keychain, JSON-file fallback off-darwin).\n  search/   Local cross-conversation index (SQLite + FTS5) and the offline\n            embedding pipeline (transformers.js).\n  mcp/      The stdio server: tool schemas, handlers, the privacy-policy loader,\n            and the experimental MCP Apps login view (mcp/ui/).\n  util/     [TAG]-prefixed logger (stderr only — stdout is the MCP JSON-RPC stream).\n```\n\nEverything Yomi writes for humans goes to **stderr**; **stdout is reserved for the\nMCP JSON-RPC stream**. A stray `console.log` corrupts the protocol — don't add one on\nany path the server can reach.\n\n---\n\n## Privacy\n\nBy default Yomi indexes **all** your conversations into the local, on-device search\nindex so an agent can search across them — a capture-all default, opt-out per chat.\nNothing is ever uploaded; the only data that leaves your device is whatever the agent\nitself surfaces in its replies. (The one non-LINE network call Yomi itself makes is the\n**one-time embedding-model download from HuggingFace** on the first search — model\nweights in, no message content out; see [Search](#search).) [`PRIVACY.md`](PRIVACY.md)\nis the canonical policy,\nand Yomi surfaces that same text to the agent on connect (and via `get_scope_policy`)\nso it can disclose the default before any bulk read.\n\n---\n\n## Disclaimer\n\nYomi is an independent, unofficial personal project, built for learning and for\naccessing one's own LINE account. It is **not affiliated with, authorized, or\nendorsed by LINE Corporation**. \"LINE\" is a trademark of its respective owner.\n\nRunning Yomi may violate LINE's Terms of Service, and operating an additional client\non an account can result in rate-limiting or suspension of that account. Yomi is\nintended for accessing **your own** account and data. You are solely responsible for\nhow you use it. The software is provided \"as is\", without warranty of any kind — see\n[`LICENSE`](LICENSE).\n\n## Acknowledgments\n\nYomi's LINE protocol implementation was written with reference to three open-source\nprojects, whose field layouts, E2EE chunk ordering, request shapes, and Thrift\ndefinitions informed this independent implementation:\n\n- **[evex-dev/linejs](https://github.com/evex-dev/linejs)** (MIT) — request shapes\n  and the Letter-Sealing E2EE payload layout.\n- **[DeachSword/CHRLINE](https://github.com/DeachSword/CHRLINE)** (BSD-3-Clause) —\n  protocol field layouts and the passwordless login flow.\n- **[er1ce/LINE-Protocol](https://github.com/er1ce/LINE-Protocol)** (Apache-2.0) —\n  Thrift enum definitions (ContentType, MessageRelationType, ServiceCode).\n\nTheir copyright notices and full license texts are reproduced in [`NOTICE`](NOTICE),\nas their licenses require.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}