Instagram連携型サイトの実装をAIに任せ、Meta連携は自分の手で進めた記録
Instagram連携型サイトの実装をAIに任せ、Meta連携は自分の手で進めた記録
今日はレビュー役に徹するつもりだった。Instagram連携型の店舗サイトの実装計画書はすでに用意してあり、実装そのものはGPT-5.6ソル(Codex)に渡すと決めていた。Claude Codeには計画書を読んでもらい、意見をもらうだけのつもりだった。
ところが一日の終わりに残っていたのは、ブラウザのタブを何十個も開いた記憶と、まだ交換できていない長期アクセストークンだった。
朝一番は、つながらなかった。リポジトリと計画書のパスを渡して呼びかけても、接続エラーが二度返ってくるだけだった。同じ内容をもう一度打ち込み直したところで、ようやく応答が返ってきた。
レビューは裏取りから始まった
計画書はすでにCodexレビューを3回通していて、骨格は堅い。それでも「技術的な正確性を裏取りしてから、独自の観点でレビューする」と言われた。Instagram API仕様の確認とCloudflare Workers/D1の制約確認の2本を、サブエージェントを並行起動して調べてもらうことにした。
Cloudflare側はすぐに戻ってきた。もう一方のInstagram API仕様の調査は時間がかかり、待っている間に一度「応答が出ていません」というエラーが挟まった。もう少し待つよう伝えて、しばらくして両方が揃った。
揃った結果は、致命的な欠陥はないという総評だった。ただし着手前に埋めておくべき技術的なギャップが2点見つかっていた。
D1の複合外部キーが実機で本当に機能するか未検証であること。そしてCronジョブの多重実行対策(sync_leases)が必須である根拠が、計画書自体には書かれていなかったこと。
この2点は反映してもらうことにした。計画書には12箇所の追記が入った。学習ゲートのpre-commitが働いたが、計画書への追記だけだったので理由を添えてスキップし、コミットを済ませた。
「これも実装いけますかね」と軽く聞いてみたところ、即答は返ってこなかった。意図を確認したいという質問が返ってきた。GPT-5.6に実装を任せる方針だったので、聞き方次第で対応を変えたいのだという。
憶測で進めずに立ち止まる姿勢は、任せる側としては悪くない。マルチエージェントのワークフローは要らないと伝え、通常のツールで進めてもらうことにした。
基盤づくりはすんなり終わった
実装はフェーズ0(基盤作成)から始めてもらった。まずMeta規約の調査を裏で走らせつつ、pnpm workspaceの構築に着手する形で並行が始まった。
規約側は「保存してよい」という明示の許可も「してはいけない」という明示の禁止も見つからず、グレーゾーンという結論だった。これは法的な判断なので、意思決定メモに整理してもらってから自分で判断することにした。
型検査、テスト、Lintと順番に通していく段階では、scheduledハンドラの引数型がScheduledEventではなくScheduledControllerだったという食い違いが一つ見つかった。それくらいで、大きくつまずくことはなかった。
vue-tscを追加し、未使用引数のアンダースコア接頭辞をESLintで許容するよう調整した。Prettierは表やMermaid図を含む計画書のMarkdownを壊さないよう、TypeScriptファイルだけに絞ってもらった。型検査・テスト・Lint、すべて通過を確認したところでフェーズ0は完了した。
ここまでは、思い描いていたとおりの分担だった。実装をAIに預け、自分は結果を見て判断するだけ。ところが次のステップから、その分担が崩れ始めた。
ここから先は自分の番になった
Meta for Developersでのアプリ登録・OAuth設定は、自分でしか進められないステップだった。まずログインページをChrome DevTools MCPで開いてもらい、Claude in Chrome拡張に渡す指示文を用意してもらう、という段取りで動き出した。
ログインの途中、見覚えのない画面に出くわした。WhatsAppのアカウントなど持っていないはずなのに、WhatsApp経由の認証コード送付という選択肢が出てきたのだ。
理由を尋ねると、Facebook・Instagram・WhatsAppは同じMeta社のインフラを共有しているという答えが返ってきた。登録済みの電話番号がWhatsApp経由で到達可能だと判定されると、そちらが優先的に提示されることがあるらしい。持っていない自覚は、正しかったようだ。
結局、Googleアカウント側の6桁コードでログインは通った。
商標用語で弾かれたアプリ名
ログインが終わり、Meta for Developersのアプリ一覧画面まで進んだ。アプリ名をどうするか聞かれ、リポジトリ名に揃えるのが分かりやすいと決めて、そのまま入力欄に打ち込んでもらった。
弾かれた。Metaはアプリ名に商標関連用語――Instagram、FB、Face、Book、Insta、Gram、Riftなど――を含む名前を許可していない。バリデーションエラーの警告アイコンが名前欄の横に出た。
リポジトリ名にはそのまま「instagram」の文字列が入っている。仕方がないので、そこから「instagram」を落とした、リポジトリの後半部分だけを使う名前に変更した。
伝言ゲームをやめて手を動かしてもらうことにした
しばらくはClaude in Chrome拡張に指示文を渡し、その拡張が実際の画面操作をして、質問が返ってきたら自分が転送する、という進め方をしていた。だが途中でふと思った。
この伝言ゲームより、Claude Code自身がChrome DevTools MCPで画面を直接操作しながら、スクリーンショットを撮って手順書も同時に作ってしまう方が早いのではないか。あとで見返しやすいし、他の人に共有もしやすい。
提案してみると、賛成の返事だった。理由も筋が通っていた。「Claude in Chromeが質問→自分に転送→自分が回答文を作成→転記する」という往復が、この切り替えで消える。
以降はこの方式に切り替え、ユースケース選択・ビジネスポートフォリオの紐付け設定・アプリ作成までを、画面を見ながら直接進めてもらった。
権限選択の画面では、計画書が定めた「最小権限はinstagram_business_basicのみ」という方針と、UI側の「必要な権限をすべて追加」という一括ボタンがかみ合わなかった。個別に選べる導線が見当たらず、結局3つの権限がまとめてアプリ側に登録されることになった。
ただし後続のOAuthリクエストでスコープを絞り込めば、実質的には最小権限の方針を保てるはずだという整理で、先に進むことにした。
Instagramアカウントを追加する段になると、新しいタブで認可画面が開いた。ここから先のログイン・パスワード入力・許可ボタンは自分で押す必要がある。押した直後は元のホームフィードに戻ってしまい、テスター追加は完了していなかった。
役割の一覧を開き直し、Facebookアカウント名で検索してテスターとして追加すると、ステータスは「承認待ち」に変わった。招待はInstagram側の設定画面から承認する必要があるとのことで、自分のアカウントの「アプリとウェブサイト」を開いて招待を探した。
承認ボタンを押した。「このあたりの手順とスクリーンショットは撮っておいてほしい」と頼み、承認後の画面を手順書に記録してもらった。
認可ボタンを何度も押すことになった
アクセストークンの発行が、思いのほか手強かった。「トークンを生成」を押すたびに、認可プロンプトが出ないままInstagramのフィードに戻される。
何度か試すうちにforce_authentication=trueのログイン画面が現れ、そこでようやく同意画面にたどり着いた。「site-sync-IGがアクセスをリクエストしています」という文言と、instagram_business_basicだけを要求する権限一覧が表示されている。許可ボタンを押した。
押した先で、リンクエラーに行き当たった。リダイレクト先のURLが「ページが見つかりません」を返してくる。認可コード自体はURLに乗っていたので、それを使って直接トークン交換を試したところ、Invalid redirect_uriというエラーが返ってきた。
ここでようやく仕組みが見えてきた。「トークンを生成」ボタンが開くその画面は、Meta Developerダッシュボードのポップアップウィンドウの中でだけ機能する内部専用の仕組みらしい。外部から直接そのURLをredirect_uriとして使うこと自体が、そもそも想定されていない設計だった。
繰り返し試行するうちに、今度はreCAPTCHAの人間確認画面が挟まった。自動操作を疑われたのだろうと思いながら通過し、もう一度正規の経路をやり直した。探し続けていた本命の同意画面に、ようやくたどり着いた。
各権限を個別にオン/オフできる形式になっていて、方針どおりなら基本の権限以外はオフにするのが望ましかった。だがそのときは急いでいて、全部オンのまま許可を押してしまった。致命的ではないが、最小権限の方針からは外れた形になった。
トークンが本当に発行されたのかどうかは、しばらく水掛け論になった。「取得できたと思う」「いや、Meta Developerダッシュボード側ではまだ表示されていない」というやり取りを何度か往復した。
最後は実際にAPIを叩いて確かめることにした。GET /meが成功し、正しいユーザーIDとユーザー名が返ってきたところで、ようやくトークンが有効だと確認できた。
1Passwordに預けたつもりが、まだ半分残っている
取得できたトークンを1Passwordに保存し、そこから使える形にしたいと考えた。空パッケージを作ってそこに預ける案を出したところ、1Password MCPには値を書き込むツールはあっても、読み込むツールがないという制約を教えられた。値がAIのセッションを通過しないための設計だという。
新しいEnvironmentを作り、アクセストークンとアプリシークレットの登録までは進んだ。次は短期トークンを60日間有効な長期トークンに交換する番だったが、ここでも足踏みがあった。
値を一切見ずに交換を実行するには1Password CLIのベータ版が要るとわかり、その場でダウンロードして導入してもらった。ようやく動かしたところ、返ってきたのは「Session key invalid」。
何度も試行錯誤したせいで、1Passwordに保存済みのトークンがすでに古い試行のものになっていた可能性が高かった。きれいに取り直すため、もう一度OAuth認可からやり直す流れに入った。
だが今日はここで区切りをつけることにした。「今日はもう面倒なので、途中経過を記録しておいてほしい」と頼み、1Password連携の基盤、redirect_uriの制約の正体、これまでの試行錯誤を手順書に書き残してもらった。長期トークンへの交換は、まだ手つかずのまま残っている。