開発book-knowledge-base

出発点:裁断できない本をどう取り込むか

book-knowledge-base には既に「読み解く」スラッシュコマンド(/yomitoku)がある。裁断本をスキャンしたPDFを yomitoku に投げ、章節単位に分割して Turso に格納する流れだ。問題は、最近 Kindle で買った本がこのパイプラインに乗らないこと。Amazon の DRM がかかっていてPDFに落ちないから、裁断本と同じ前処理を踏めない。

そこで「read.amazon.co.jp の Kindle Cloud Reader をスクショで巡回して、画像群を既存パイプラインに流せばいい」という発想に切り替えた。OCRから先は /yomitoku がそのまま使える。前段の「画像を集めるところ」だけを新規で組めばよい。

朝のうちはこのアイデアを温めるだけのつもりだったが、気がついたら半日溶けていた。

採用方針:C案ハイブリッド(スラッシュコマンド × Chrome拡張)

最初に「スラッシュコマンド単体 vs Chrome拡張機能単体 vs ハイブリッド」の3案を並べた。比較した結果、ハイブリッドのC案で進めることにした。

  • スラッシュコマンド単体: Chrome DevTools MCP を使えばページめくり・スクショ・OCR 委譲まで一気通貫で書ける。会話で「○ページまで」「OCR込み/抜き」を毎回指示変更できる柔軟さがある。一方で、長時間のページ送りループを MCP セッションに張り付けて回すのは不安定になりがち。
  • Chrome拡張機能単体: アイコンクリックで起動、ブラウザ内のDOM操作とスクショに強い。ただし、保存先ディレクトリやページ数の指定をUIに毎回作り込まないと変更できない。
  • C案ハイブリッド(採用): ① スラッシュコマンドで起動 → ② Chrome拡張機能が Kindle Reader のページめくりとスクショを担当 → ③ ダウンロードされた画像群を /yomitoku パイプラインに流して OCR/章節分割/Turso 登録。

C案を選んだのは、動的な指示性(スラッシュコマンド側)とページ操作の安定性(拡張機能側)を両取りできるから。Kindle のページめくりは拡張機能のバックグラウンドに任せて、スラッシュコマンドは前後の段取りだけ見る、という分業にした。

準備:Chrome DevTools MCP が朝から壊れていた

実装に入る前に Chrome DevTools MCP が動かない。mcp__chrome-devtools__* のツールが一切ロードされない。ツール検索しても空振りする。

~/.claude/rules/windows.md の「最頻の詰まり」に該当していた。9222 ポートで curl -s http://localhost:9222/json/version を叩くと壊れた残骸が返ってくる。壊れた残骸の9222リスナーが居座っているのが真因。本来は chrome.exe を全部落として temp profile + 9222 で起動し直し、/mcp で chrome-devtools を reconnect すれば復活するはずだった。

ところがこの日は別の問題も重なっていた。.claude.json から chrome-devtools MCP の登録そのものが消えていた。バックアップ(3/21時点)を覗くと24箇所に書かれていたものが、現在は0件。3/21〜6/17 のどこかで設定ファイルが書き換えられて消えた、というのが正体だった。

仕方ないので claude mcp add で user スコープに再登録。Git Bash の MSYS パス変換で /cC:/ に化けて起動失敗するトラップにも引っかかったが、修正してリトライ。さらにこの先 .claude.json が消えても気づけるよう、MCP サーバー設定だけ抽出して ~/.claude/mcp-servers-snapshot.json として git 管理に入れた。

Chrome 149 stable で remote debugging を許可する方法も、ログイン済み Chrome に繋ぐために必要だった。chrome://inspect/#remote-debugging で incoming debugging connections を Allow に切り替え、--autoConnect 方式に変更。Claude Code を再起動してようやく mcp__chrome-devtools__* が見えるようになった。

ここまでで午前中が終わった。

プローブ:Kindle Cloud Reader の DOM を覗く

復活した MCP で Kindle のタブに切り替えてスクショを撮ったところ、ちゃんと撮れた。ASIN/タイトル/Location/「Back to 309」(しおり)が DOM から取れることまで確認。

ArrowRight で 20→22 へ進む(+2/ページ)、ArrowLeft で 22→8→2 まで戻せる。先頭 Location 2 まで到達できたので、巡回の最小単位は動くことが分かった。

そこから先で UI ノイズ(「Back to 309」表示、下部スクロールバー)を CSS で隠してテスト撮影。クリーンな状態が取れたので、ここで「5ページ分連続で進めるループ」に入ろうとした矢先、設計議論に戻すことにした。

「ループは MCP セッションで回し続けるより、Chrome 拡張機能のバックグラウンドに任せた方が安定する」と判断したから。C案の本気の実装に切り替えた。

実装:chrome-extension-kindle を新規ディレクトリで切る

C:\Users\numbe\Git_repo\chrome-extension-kindle を新規作成して git init。既存の chrome-extension-MF(クラウド会計)や chrome-extension-x(X/Twitter)と同じ命名規則に揃えた。Chrome 拡張機能は 全て別々のリポジトリで管理 している慣習に乗る形だ。

並列でファイル群を生成させた。manifest.json、popup.html、popup.js、content.js、background.js、offscreen.html/js、アイコン群。スラッシュコマンドは book-knowledge-base 側に /yomitoku-kindle として追加。既存 /yomitoku に画像群を渡すだけのアダプタに留めた。

popup.js の innerHTML を安全な DOM 構築に置き換える等の細かい修正は途中で挟んだが、骨格は数回のターンで揃った。

試行錯誤のフェーズ:5つの壁にぶつかった

ここからが本番で、E2E で動かそうとするたびに別の問題が顔を出した。

壁1: 文字化けと「位置: 不明」

最初に動かした拡張機能は、ページ情報が「位置: 不明」になり、書名も文字化けしていた。原因を Chrome DevTools 経由で直接調査したら、Kindle Cloud Reader 側で UTF-8 → Latin-1 の mojibake が起きていた。TextDecoder で復元できることを確認し、content.js に組み込んで「明治維新とは何だったのか――世界史から考える」相当のタイトルが綺麗に取れるようになった。

壁2: 拡張機能がリストに出てこない

content.js を直してリロードしてもらったが、chrome://extensions/ の拡張機能リストに「Kindle Cloud Reader Capturer」が出てこない。ユーザーが「リロードしたよ」と言ってくれたタイミングと、こちら側で MCP から状態を覗くタイミングがずれていた可能性が高い。

何度もスクショを撮ってもらいながら、最終的にロードできた。「リロード」が拡張機能のリロードなのか Kindle タブのリロードなのか、口頭で曖昧になりやすいことを学んだ。

壁3: Reader did not become ready (slider not found) in 60s

巡回ループに入ると、60秒経って Reader did not become ready (slider not found) in 60s のエラーで止まる。「進んでないんで、ページの画面が多分止まってます」というユーザーの観察が正しかった。

調査したら、対象書籍が 固定レイアウト(fixed layout)書籍 だった。本文が .kg-full-page-img という画像要素として配信されていて、Kindle が "Learning reading speed..." モードに居る間 reader-footer-title が空のまま。slider が出てくる前提のロジックが破綻していた。

ページ送りは「Next/Previous page ボタンを直接クリック」する方式に切り替え、終端判定は画像の fingerprint(同じページが連続したら終端)で組み直した。

壁4: 進捗が見えない

popup を開いても「実行中」とだけ出て、動いているのか止まっているのか分からない。「動いてるのか止まってんのかよくわかりません」という指摘を受けて、popup.js にリアルタイム進捗表示を入れた。ページ番号と取得済み枚数が刻々と更新されるようにして、ユーザーが「進んでない」を画面で判定できるようにした。

壁5: ファイル名が UUID になる + 3枚で落ちる

「3枚今落ちたんすかね」と聞かれて Downloads フォルダを覗いたら、ダウンロード.png という名前のファイルが UUID 付きでばらまかれていた。kindle-captures/<ASIN>/page_NNNN.png の指定が完全に無視されていた。

さらに進めたら今度は URL.createObjectURL is not a function のエラーが popup に一瞬出る。MV3 の service worker から URL.createObjectURL が削除されている仕様にぶつかっていた。専用の Offscreen Document を立てて、そこで blob URL を作る回避策に書き換えた。

ファイル名の方は、blob URL を chrome.downloads.download に渡すと filename 引数が無視される仕様だったので、chrome.downloads.onDeterminingFilename リスナーで強制上書きするように変更。UUID 名で散らばっていた 69 個の PNG を掃除してから、ようやく kindle-captures/<ASIN>/page_NNNN.png の形でサブフォルダに連番保存されるようになった。

着地:v0.2 で「一応うまくいってる」状態

夕方になって、ようやく popup から実行ボタンを押すと、Kindle のページが順番に送られて、Downloads の kindle-captures/<ASIN>/ 配下に page_0001.png から連番で溜まる状態になった。OCR とTurso 登録は明日に持ち越し。

「OK。一応うまくいってるみたいなんで、ここまでの進捗ちょっとドキュメンタリー残しておいてください。また明日やりましょう」で締めて、計画書に今日の格闘記録(5つの壁と解決方法)と拡張機能 v0.2 の最終構成、残課題、明日の復帰プロンプトをまとめて書き残してもらった。

学びメモ

  • 「MCPで一気通貫」より「拡張機能に逃がす」が正解だった瞬間が確かにあった。MCP セッションで長時間ループを回し続けると不安定になる。バックグラウンドに任せられる仕事は拡張機能側に分離した方が、観察と再開がしやすい。
  • 進捗の可視化を後回しにすると、進んでいるか止まっているかが分からなくなる。popup に進捗表示を入れた瞬間、デバッグの解像度が一段上がった。最初から組むべきだった。
  • MV3 の制約は service worker の制約URL.createObjectURL が使えない、localStorage が使えない、等の罠が随所にある。Offscreen Document はその回避策として覚えておく。
  • Kindle の本には固定レイアウトとリフロー型がある。固定レイアウトは本文が画像として配信されるので、テキスト抽出ではなく OCR 一択になる。今回の対象書籍はたまたま固定レイアウトだったが、リフロー型は別の DOM 取得経路が要る。
  • .claude.json のMCP 設定はバージョン管理されていないので、消えても気づきにくい。今日抽出した ~/.claude/mcp-servers-snapshot.json を git に入れたので、次に何か消えても git log で追える。

明日やること

  • OCR連携: 拡張機能が保存した kindle-captures/<ASIN>/page_*.png/yomitoku に渡して章節分割まで通す
  • Turso 登録: book-knowledge-base の既存スキーマに乗せて、Kindle由来かPDF由来かを区別するフラグを追加する
  • リフロー型書籍での挙動を別の本で1冊試す(固定レイアウトしかテストできていない)
  • スラッシュコマンド /yomitoku-kindle <URL> から拡張機能起動 → 完了通知 → OCR まで自律的に連結する部分の検証