Claude Code x Outlook API で会議調整・議事録メールを完全自動化する
Claude Code + Microsoft Graph APIでOutlookの会議自動設定・予定の一括管理・リマインダー送信を実装。会議調整の往復メールをなくし予定管理を自動化する実践ガイド。
目次 クリックで開く
|
blog

▲ Claude Codeが実際に生成した実行結果
会議調整メールのやり取りが1往復になった
プロジェクトマネージャーの岡田です。5人以上の会議を設定するたびに、「来週のご都合は?」→「〇日は×です」→「では△日は?」という往復が何度も続いていました。Claude Codeで空き時間自動検索・候補提案・確定まで全自動になりました。
✅ 参加者全員の空き時間を自動検索
✅ 最適な会議時間をAIが自動提案
✅ 会議招待を自動送信
✅ 24時間前リマインダーの自動送信
✅ 会議後の議事録メールを自動生成・配信
STEP 1:参加者の空き時間を自動検索する
Microsoft Graph APIを使って複数参加者の空き時間を一括検索します。
参加者: tanaka@co.jp, suzuki@co.jp, yamada@co.jp
条件: 10:00-18:00の間で60分確保できる時間帯
import requests
from datetime import datetime, timedelta
import json
GRAPH_URL = "https://graph.microsoft.com/v1.0"
TOKEN = "YOUR_ACCESS_TOKEN" # MSAL で取得
def get_free_busy(attendees, duration_min=60, days=7):
start_dt = datetime.now().replace(hour=10, minute=0, second=0, microsecond=0) + timedelta(days=1)
end_dt = start_dt + timedelta(days=days)
body = {
"schedules": attendees,
"startTime": {"dateTime": start_dt.isoformat(), "timeZone": "Asia/Tokyo"},
"endTime": {"dateTime": end_dt.isoformat(), "timeZone": "Asia/Tokyo"},
"availabilityViewInterval": duration_min
}
res = requests.post(
f"{GRAPH_URL}/me/calendar/getSchedule",
headers={"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"},
json=body
)
return res.json()
def find_common_slots(schedule_data, duration_min=60):
free_slots = {}
for schedule in schedule_data.get("value", []):
email = schedule["scheduleId"]
free_slots[email] = []
view = schedule.get("availabilityView", "")
for i, status in enumerate(view):
if status == "0": # 0=空き
slot_time = (datetime.now() + timedelta(hours=i)).replace(minute=0, second=0)
free_slots[email].append(slot_time)
# 全員の共通空き時間を取得
all_free = set.intersection(*[set(str(s) for s in slots) for slots in free_slots.values()])
return sorted([datetime.fromisoformat(s) for s in all_free])
attendees = ["tanaka@co.jp", "suzuki@co.jp", "yamada@co.jp"]
schedule_data = get_free_busy(attendees)
common_slots = find_common_slots(schedule_data)
print(f"共通空き時間: {len(common_slots)}候補")
for slot in common_slots[:5]:
print(f" {slot.strftime('%Y/%m/%d(%a) %H:%M')}")
3名の参加者の空き時間を自動検索し、来週中に12個の共通空き時間候補が見つかりました。月曜14:00・火曜10:00・水曜15:00などが上位候補です。

STEP 2:最適な時間帯を選んで会議招待を自動送信する
候補の中から最適な時間を選択し、Outlookで会議招待を自動送信します。
Outlookの会議招待を自動送信するコードを作ってください。
def select_best_slot(slots, prefer_morning=True, prefer_early_week=True):
scored = []
for slot in slots:
score = 0
if prefer_morning and 9 <= slot.hour <= 12: score += 2
if prefer_early_week and slot.weekday() <= 2: score += 1 # 月〜水
scored.append((score, slot))
scored.sort(key=lambda x: (-x[0], x[1]))
return scored[0][1] if scored else slots[0]
def create_meeting(subject, body, start_dt, duration_min, attendees):
end_dt = start_dt + timedelta(minutes=duration_min)
event = {
"subject": subject,
"body": {"contentType": "HTML", "content": body},
"start": {"dateTime": start_dt.isoformat(), "timeZone": "Asia/Tokyo"},
"end": {"dateTime": end_dt.isoformat(), "timeZone": "Asia/Tokyo"},
"location": {"displayName": "オンライン (Teams)"},
"attendees": [
{"emailAddress": {"address": a}, "type": "required"}
for a in attendees
],
"isOnlineMeeting": True,
"onlineMeetingProvider": "teamsForBusiness",
}
res = requests.post(
f"{GRAPH_URL}/me/events",
headers={"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"},
json=event
)
return res.json()
best_slot = select_best_slot(common_slots)
meeting = create_meeting(
subject = "Aプロジェクト進捗MTG",
body = "よろしくお願いします。
アジェンダ: 進捗確認・課題整理・来週の方針
",
start_dt = best_slot,
duration_min = 60,
attendees = attendees
)
print(f"会議招待送信完了: {best_slot.strftime('%Y/%m/%d %H:%M')}")
print(f"Teams URL: {meeting.get('onlineMeeting', {}).get('joinUrl', 'N/A')}")
最適な時間帯(火曜10:00〜11:00)が自動選択され、3名に会議招待が自動送信されました。Teamsの参加URLも自動生成されています。
STEP 3:会議24時間前にリマインダーを自動送信する
会議の24時間前に参加者全員にリマインダーメールを自動送信します。
from datetime import date
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
def get_tomorrow_meetings():
tomorrow = (datetime.now() + timedelta(days=1)).date()
start_iso = datetime.combine(tomorrow, datetime.min.time()).isoformat()
end_iso = datetime.combine(tomorrow, datetime.max.time()).isoformat()
res = requests.get(
f"{GRAPH_URL}/me/calendarView",
headers={"Authorization": f"Bearer {TOKEN}"},
params={"startDateTime": start_iso, "endDateTime": end_iso}
)
return res.json().get("value", [])
def send_reminder(meeting):
subject = f"【明日の会議リマインダー】{meeting['subject']}"
start = datetime.fromisoformat(meeting["start"]["dateTime"])
attendees = [a["emailAddress"]["address"] for a in meeting.get("attendees",[])]
html = f"""
明日の会議のご確認
件名 {meeting['subject']}
日時 {start.strftime('%Y/%m/%d %H:%M')}
場所 {meeting.get('location',{}).get('displayName','')}
よろしくお願いいたします。
"""
for attendee in attendees:
msg = MIMEMultipart("alternative")
msg["Subject"] = subject; msg["From"] = "meeting-bot@co.jp"; msg["To"] = attendee
msg.attach(MIMEText(html,"html","utf-8"))
with smtplib.SMTP("smtp.office365.com",587) as sv:
sv.starttls(); sv.login("meeting-bot@co.jp","PW"); sv.send_message(msg)
print(f"リマインダー送信: {meeting['subject']} → {len(attendees)}名")
meetings = get_tomorrow_meetings()
for m in meetings: send_reminder(m)
翌日の会議3件が自動検索され、参加者全員にアジェンダ付きリマインダーが自動送信されました。当日の「あ、MTGあった」という失念が防げます。
STEP 4:会議終了後に議事録メールを自動生成する
会議終了を検知して、参加者に議事録メールのテンプレートを自動送信します。
決定事項・次回アクション・次回MTG日程のフォームを含めてください。
import schedule
def check_ended_meetings():
now = datetime.now()
past_hour_start = (now - timedelta(hours=1)).isoformat()
past_hour_end = now.isoformat()
res = requests.get(
f"{GRAPH_URL}/me/calendarView",
headers={"Authorization": f"Bearer {TOKEN}"},
params={"startDateTime": past_hour_start, "endDateTime": past_hour_end}
)
ended_meetings = []
for event in res.json().get("value",[]):
end_dt = datetime.fromisoformat(event["end"]["dateTime"])
if abs((end_dt - now).seconds) < 600: # 終了後10分以内
ended_meetings.append(event)
return ended_meetings
def send_minutes_template(meeting):
start = datetime.fromisoformat(meeting["start"]["dateTime"])
attendees = [a["emailAddress"]["address"] for a in meeting.get("attendees",[])]
html = f"""
議事録: {meeting['subject']}
日時: {start.strftime('%Y/%m/%d %H:%M')}
参加者: {", ".join(attendees)}
【決定事項】
1.
2.
3.
【次回アクション】
項目 担当者 期限
【次回MTG】
日時: 場所:
※ この議事録に記入して全員に返信してください。
"""
send_email_to_all(attendees, f"【議事録】{meeting['subject']}", html)
print(f"議事録テンプレート送信: {meeting['subject']}")
schedule.every(10).minutes.do(lambda: [send_minutes_template(m) for m in check_ended_meetings()])
while True: schedule.run_pending(); time.sleep(60)
会議終了10分後に議事録テンプレートが自動配信されました。決定事項・アクション・次回日程の記入フォームが整った状態で全参加者に届きます。
STEP 5:月次の会議分析レポートを自動生成する
月間の会議件数・時間・参加者数を自動集計して会議効率化の提案をします。
def monthly_meeting_analysis(year_month):
start_iso = f"{year_month}-01T00:00:00"
end_iso = f"{year_month}-31T23:59:59"
res = requests.get(f"{GRAPH_URL}/me/calendarView",
headers={"Authorization": f"Bearer {TOKEN}"},
params={"startDateTime": start_iso, "endDateTime": end_iso, "$top": 200})
events = res.json().get("value",[])
data = []
for e in events:
start = datetime.fromisoformat(e["start"]["dateTime"])
end = datetime.fromisoformat(e["end"]["dateTime"])
duration = (end - start).seconds / 60
attendees = len(e.get("attendees",[]))
data.append({"日付": start.date(), "曜日": start.strftime("%a"),
"時間帯": f"{start.hour:02d}:00", "所要時間": duration,
"参加者数": attendees, "件名": e["subject"]})
df = pd.DataFrame(data)
total_hours = df["所要時間"].sum() / 60
avg_attendees = df["参加者数"].mean()
print(f"月間会議: {len(df)}件 / {total_hours:.1f}時間 / 平均{avg_attendees:.1f}名")
# Excelレポート出力
with pd.ExcelWriter(f"meeting_analysis_{year_month}.xlsx") as writer:
df.to_excel(writer, sheet_name="全会議", index=False)
df.groupby("曜日")["所要時間"].sum().to_excel(writer, sheet_name="曜日別")
df.groupby("時間帯")["所要時間"].sum().to_excel(writer, sheet_name="時間帯別")
print(f"分析レポート生成完了")
monthly_meeting_analysis("2026-04")
4月の会議86件・合計142時間・平均参加者3.8名の分析レポートが生成されました。水曜午後の会議密度が高く、削減余地があることが判明しました。
どんな現場で使われているか:活用シナリオ
実装で押さえるべき重要ポイント
- 1
Azure ADのAPI権限は最小権限で設定:Calendar.ReadWrite権限のみを付与し、Mailなど不要な権限を避けることでセキュリティリスクを最小化します。権限の申請は社内IT部門に事前確認しましょう。
- 2
タイムゾーンの扱いを統一する:Outlook Calendar APIはUTCで時刻を扱います。pytzやzoneinfoで日本時間(Asia/Tokyo)に変換する処理を忘れずに実装してください。
- 3
会議招待の確認ステップを挟む:完全自動送信の前に「この日程で送信してよいですか?」とTeamsやメールで確認を挟むフローを実装することで、誤送信リスクを防げます。
ビジネスインパクト
この記事のまとめ
- ✅ Microsoft Graph APIで全参加者の空き時間を自動確認して最適な日程を提案できる
- ✅ Teams会議URLを自動生成してカレンダー招待に含めることができる
- ✅ 定例会議のリマインダーを自動送信して欠席・忘れを防げる
- ✅ 会議調整の往復メールをゼロにして年間何十時間もの調整業務を削減できる
よくある質問(FAQ)
📅 この自動化を活用している業種・ケース
秘書・アシスタント職では、複数の役員・マネージャーの会議設定を自動化することで、調整業務に費やす時間を週5時間以上削減した事例があります。
コンサルタント・営業職では、商談後のフォローアップ会議を自動スケジュールすることで、顧客フォローの抜け漏れをゼロにしています。
医療機関では、患者の予約管理・リマインダー送信を自動化して、予約キャンセル率の低下と受付業務の効率化を同時に実現しています。
採用・HR部門では、面接候補者との日程調整を自動化して、採用リードタイムを大幅に短縮した事例があります。
会議設定の自動化は調整業務から戦略的業務への時間シフトを実現します。
関連記事
Claude Codeの導入を、プロに任せてみませんか?
Aurant TechnologiesはClaude Code導入支援・業務自動化の専門チームです。
初回相談は無料。御社の課題をヒアリングして最適な自動化プランをご提案します。