Zoomのミーティング、1件ずつポチポチ登録していること、ありませんか?
3件くらいなら「まぁ、いいか」ってなるんですけど、講座の予約を7件とか8件とか入れないといけないとき、しかも全部に招待者を追加しないといけないとき……もう、心が折れそうになりますよね。
Claude Codeとの対話だけで、CSVからZoomミーティングを一括登録して、Googleカレンダー経由で招待メールまで送れるローカルアプリを作りました。
開発にかかった時間は、およそ3時間。
コードは1行も自分で書いていません。Claude Codeに「こうしたい」「ああしたい」と伝えていっただけ。そのやり取りの過程を記録しておきます。
きっかけは「7件のZoomミーティングに招待者を追加する作業」
講座用のZoomミーティングが7件ありまして。
第1講〜第7講まで、それぞれに受講者のメールアドレスを招待者として登録する必要がありました。
1件ずつZoomの管理画面を開いて、メールアドレスをコピペして、保存して、次のミーティングを開いて……。
「これ、面倒だなぁ」
面倒と感じたら、自動化したくなるのがエンジニア。
というわけで、Claude Codeに相談してみたんです。
まずはCSVから一括登録するUIを作った
最初は「CSVを読み込んで、Zoomミーティングを一括登録するWebアプリがほしい」という、ざっくりしたリクエストから始めました。
Claude Codeが最初に作ってくれたのは、和風デザインのHTML。判子アイコンに明朝体フォント。
「いや、実用的なデザインにして。和の要素は不要」
と伝えたら、すぐにInter + JetBrains MonoのモダンなUIに変わりました。ブルー基調の落ち着いた業務ツールっぽいデザイン。いい感じ。
3ステップのウィザード形式
できあがったUIは、3ステップ構成です。
- CSV読み込み — ファイルドロップまたはテキスト直接入力
- 内容確認 — パース結果をテーブル表示、チェックボックスで選択
- 登録完了 — 結果一覧とJoin URLの表示
日付形式は 2026-04-17 だけじゃなく 2026/04/17 にも対応してもらいました。スラッシュ区切りもよく使いますから。
ローカルアプリでいいやん
最初はCloudflare Workerとしてデプロイする前提で進んでいたんですが、途中で気づきました。
「あっ、Webアプリじゃなくていいの。ローカルで動けば十分。」
ということで、シンプルなNode.jsのローカルサーバーに方針転換。node server.js で起動して、http://localhostにアクセスするだけ。
1Passwordから認証情報を自動取得
ここがちょっとこだわったポイント。Zoom APIのClient IDやSecretを .env ファイルに書くのではなく、1Password CLI(opコマンド)から起動時に自動取得する仕組みにしました。
function getSecret(item, field) {
return execSync(`op read "op://Development/${item}/${field}"`, { encoding: 'utf-8' }).trim();
}
サーバー起動時にこんな感じで認証情報を読み込みます。
1Password から認証情報を取得中...
Zoom: OK
Google Calendar: OK (OAuth2)
Zoom 一括予約サーバー起動中: http://localhost:xxxx
安心感がありますよね。秘密情報をファイルに残こさずに処理できます。
Zoom APIとの格闘
スコープが足りない問題
Zoom S2S(Server-to-Server)OAuthアプリを作って、1Passwordに認証情報を保存。ここまでは順調。
で、いざミーティング一覧を取得しようとしたら……
Invalid access token, does not contain scopes:[meeting:read:list_meetings]
ここでハマりました。
Zoom Marketplaceでアプリのスコープを追加する必要があったんです。しかも一覧取得と個別取得で別のスコープが必要という。
最終的に必要だったスコープはこちら:
meeting:read:list_meetings— 一覧取得meeting:read:meeting— 個別詳細取得meeting:write:meeting— 作成meeting:update:meeting— 更新meeting:delete:meeting— 削除
1つずつエラーを見ながら追加していった感じです。
定期ミーティングの罠
招待者を取得しようとしたら、start_time が undefined になる問題が発生。
調べてみると、定期ミーティング(recurring meeting)の場合、start_time ではなく occurrences という配列に各回のスケジュールが入っているんですね。
if (detail.occurrences && detail.occurrences.length > 0) {
const futureOccurrences = detail.occurrences
.filter(o => o.start_time >= now && o.status === 'available');
// 各回分を処理
} else if (detail.start_time) {
// 通常のミーティング
}
Claude Codeがこの分岐をサッと書いてくれて、「なるほど、そういう構造なのか」と勉強になりました。
Google Calendar連携で招待メールを送る
Zoomの meeting_invitees にメールアドレスを追加しても、招待メールは自動送信されないということが判明。
じゃ、どうすれば、いいのか?
Google Calendarにイベントを作って、sendUpdates: 'all' を指定すれば、Googleカレンダー経由で招待メールが送られる。Zoom Join URLを本文に含めておけば、受け取った人はそこからミーティングに参加できるという流れを。
サービスアカウントではダメだった
最初はGoogleのサービスアカウントで認証しようとしました。
unauthorized_client: Client is unauthorized to retrieve access tokens using this method
個人のGmailアカウント(Google Workspace以外)では、サービスアカウントの「ドメイン全体の委任」が使えないんですね。OAuth2クライアント認証に切り替える必要がありました。
OAuth2の初回認証スクリプト
OAuth2認証の場合、初回だけブラウザでログインしてリフレッシュトークンを取得する必要があります。これ用の専用スクリプト auth-google.js を作りました。
node auth-google.js
実行するとURLが表示されて、ブラウザで認証すると、リフレッシュトークンが自動的に1Passwordに保存される仕組み。
ふぅ。これでようやくGoogle Calendar連携が動くようになりました。
タイムゾーンの二重変換問題
「13時開始のミーティングが、Googleカレンダーで早朝4時になっている!」
9時間ずれ。UTC→JSTの二重変換が原因でした。
toISOString() がUTC文字列(Z付き)を返すのに、Google Calendar APIに timeZone: 'Asia/Tokyo' を指定していたので、UTC時刻をJSTとして解釈してしまっていたんです。
修正後は、+09:00 付きのJST文字列を直接生成するようにしました。
function toJSTString(date) {
const jst = new Date(date.getTime() + 9 * 60 * 60 * 1000);
// ...
return `${y}-${mo}-${d}T${h}:${mi}:${s}+09:00`;
}
タイムゾーン、難しい、、です。
完成したツールの機能まとめ
最終的に、こんな機能を持つツールが完成しました。
新規登録タブ
- CSVファイルのドロップまたはテキスト入力
- バリデーション(日付・時間・会議名のチェック)
- チェックボックスで登録対象を選択
- Zoomミーティング一括作成
- Google Calendar経由で招待メール自動送信
招待者追加タブ
- Zoomから予定済みミーティング一覧を取得
- キーワード絞り込み(会議名・日時・IDで検索)
- 表示中のミーティングの招待者を一括取得
- 招待者一括追加(Zoom + Googleカレンダー招待メール)
- 招待メールのみ送信モード(Zoom側は変更しない)
- ミーティング一括削除(確認ダイアログ付き)
ファイル構成
zoom-scheduler/
├── server.js # Node.js サーバー(Zoom API + Google Calendar API)
├── auth-google.js # Google OAuth2 初回認証スクリプト
├── package.json
├── run.sh # メニュー式起動スクリプト
└── public/
└── index.html # フロントエンド UI
たった5ファイル。シンプルな構成です。
Claude Codeとの開発で感じたこと
「対話で開発」のリアル
今回のやり取りで印象的だったのは、エラーを貼るだけで解決策が返ってくること。
Zoom APIのスコープエラー、Google認証のエラー、タイムゾーンのずれ……。ターミナルのエラーメッセージをそのまま貼ると、「あ、これはスコープが足りないですね」「OAuth2に切り替えましょう」と返ってくる。
自分で調べる時間がほぼゼロ。
ドキドキするくらいスムーズでした。
方針転換も柔軟
「Cloudflare Workerで」→「やっぱりローカルアプリで」→「サービスアカウントで」→「OAuth2に切り替えて」
こういう方針転換を何度もしましたが、Claude Codeは嫌な顔ひとつせず(顔ないけど)、すぐに対応してくれました。
ハマりポイントは人間側の知識不足
技術的なハマりポイントのほとんどは、ZoomやGoogleの管理画面での設定漏れでした。スコープの追加、OAuth同意画面の設定、テストユーザーの追加……。
コード側の問題はClaude Codeがすぐ解決してくれるけど、外部サービスの設定画面は自分で操作する必要があり、このあたりの理解不足からのトライエラーでした。
起動方法
cd ~/zoom-scheduler
node server.js
ブラウザで http://localhost:xxxx を開くだけ。
1Passwordにサインインしていれば、認証情報は自動で読み込まれます。
まとめ
- CSVからZoomミーティングを一括登録できる
- 既存ミーティングに招待者を一括追加できる
- Google Calendar経由で招待メールを自動送信できる
- ミーティングの一括削除もできる
- 認証情報は1Passwordで安全に管理
- 開発はClaude Codeとの対話だけ、コード手書きゼロ
Zoomのミーティング管理で「もうちょっと楽にならんかな」と思っている方、Claude Codeとの対話で自分専用のツールを作ってみる、という選択肢もありますよーという実例でした。







