なぜ私はClaude Codeに会議業務を任せようと思ったか
Aurant Technologies でクライアント向けのシステム導入を担当する私は、毎週30本以上の社内外Microsoft Teams会議をこなしています。そしてある月曜日の朝、「今週の会議調整・議事録・フォローアップに一体何時間かけているんだろう」と計算してみたところ、週23時間という衝撃的な数字が出ました。
そのタイミングで出会ったのが Claude Code です。Claude Code はAnthropicが提供するAIコーディングエージェントで、日本語で「〇〇するコードを書いて」と依頼するだけで動作するPythonスクリプトを生成してくれます。「これをTeams業務に使えるんじゃないか」と思い立ち、2025年10月から半年かけて段階的に自動化を進めてきました。
この記事では、Claude Code に実際に送ったプロンプト・生成されたコード・実行結果をすべて公開しながら、6つの自動化シナリオを紹介します。プログラミング経験がほとんどない方でも再現できるよう、セットアップ手順も含めて記載しています。
まず最初に取り組んだのが「会議の作成」そのものの自動化です。毎回Teamsのカレンダーを開いてタイトルを入力して参加者を選んで…という操作を、スクリプト1本で済ませられるようにしました。
Claude Code にこう頼みました:
会議のタイトル・開始日時・終了日時・参加者のメールアドレスリストを引数で受け取り、
会議を作成して参加者に招待メールを送る関数を実装してください。
Azure ADアプリの認証(クライアントシークレット認証)も含めてください。
エラーハンドリングとロギングも入れて、本番環境で使えるコードにしてください。
承知しました。Microsoft Graph API を使って Teams 会議を作成する Python コードを書きます。Azure AD のクライアントシークレット認証(アプリケーション権限)を使う構成です。必要なスコープは
Calendars.ReadWrite と OnlineMeetings.ReadWrite です。
#!/usr/bin/env python3
# create_teams_meeting.py
# Claude Code が生成:Teams会議をGraph APIで自動作成するスクリプト
import os
import logging
from datetime import datetime
from typing import List, Optional
import requests
from msal import ConfidentialClientApplication
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s"
)
logger = logging.getLogger(__name__)
TENANT_ID = os.environ["AZURE_TENANT_ID"]
CLIENT_ID = os.environ["AZURE_CLIENT_ID"]
CLIENT_SECRET = os.environ["AZURE_CLIENT_SECRET"]
GRAPH_SCOPE = ["https://graph.microsoft.com/.default"]
def get_access_token() -> str:
"""Azure AD クライアントシークレット認証でトークンを取得"""
app = ConfidentialClientApplication(
CLIENT_ID,
authority=f"https://login.microsoftonline.com/{TENANT_ID}",
client_credential=CLIENT_SECRET,
)
result = app.acquire_token_for_client(scopes=GRAPH_SCOPE)
if "access_token" not in result:
raise RuntimeError(f"トークン取得失敗: {result.get('error_description')}")
logger.info("アクセストークン取得成功")
return result["access_token"]
def create_teams_meeting(
organizer_user_id: str,
title: str,
start_dt: datetime,
end_dt: datetime,
attendees: List[str],
body_content: Optional[str] = None,
time_zone: str = "Asia/Tokyo",
) -> dict:
"""
Graph API でTeams会議(オンライン会議リンク付き)を作成する。
Args:
organizer_user_id: 主催者のAzure AD ユーザーID(またはメールアドレス)
title: 会議タイトル
start_dt: 開始日時(タイムゾーンなしdatetimeはtime_zoneに従う)
end_dt: 終了日時
attendees: 参加者メールアドレスのリスト
body_content: 招待メール本文(省略可)
time_zone: タイムゾーン文字列(デフォルト: Asia/Tokyo)
Returns:
作成されたイベントオブジェクト(dict)
"""
token = get_access_token()
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
}
attendee_list = [
{
"emailAddress": {"address": email, "name": email.split("@")[0]},
"type": "required",
}
for email in attendees
]
payload = {
"subject": title,
"start": {
"dateTime": start_dt.strftime("%Y-%m-%dT%H:%M:%S"),
"timeZone": time_zone,
},
"end": {
"dateTime": end_dt.strftime("%Y-%m-%dT%H:%M:%S"),
"timeZone": time_zone,
},
"attendees": attendee_list,
"isOnlineMeeting": True,
"onlineMeetingProvider": "teamsForBusiness",
"body": {
"contentType": "HTML",
"content": body_content or f"<p>{title} にご参加ください。</p>",
},
}
url = f"https://graph.microsoft.com/v1.0/users/{organizer_user_id}/events"
logger.info(f"会議作成リクエスト送信: {title}")
try:
resp = requests.post(url, headers=headers, json=payload, timeout=30)
resp.raise_for_status()
event = resp.json()
logger.info(f"会議作成完了 ID: {event['id'][:20]}...")
logger.info(f"参加URL: {event.get('onlineMeeting', {}).get('joinUrl', 'N/A')}")
return event
except requests.exceptions.HTTPError as e:
logger.error(f"Graph API エラー {e.response.status_code}: {e.response.text}")
raise
# ---- 実行例 ----
if __name__ == "__main__":
event = create_teams_meeting(
organizer_user_id="me@aurant-technologies.com",
title="2026年5月 月次レビュー",
start_dt=datetime(2026, 5, 10, 10, 0),
end_dt=datetime(2026, 5, 10, 11, 0),
attendees=[
"tanaka@aurant-technologies.com",
"suzuki@aurant-technologies.com",
"yamada@aurant-technologies.com",
],
)
print(f"作成完了: {event['subject']}")
print(f"参加リンク: {event['onlineMeeting']['joinUrl']}")

▶ 実行結果
2026-04-25 10:14:02 [INFO] アクセストークン取得成功
2026-04-25 10:14:03 [INFO] 会議作成リクエスト送信: 2026年5月 月次レビュー
2026-04-25 10:14:04 [INFO] 会議作成完了 ID: AAMkADQwMDU5ZTY3…
2026-04-25 10:14:04 [INFO] 参加URL: https://teams.microsoft.com/l/meetup-join/19%3A…
作成完了: 2026年5月 月次レビュー
参加リンク: https://teams.microsoft.com/l/meetup-join/19%3Ameeting_NjBkZW…
Claude Code が生成したコードを実行すると、2秒でTeams会議が作成され、参加者全員に招待メールが届きました。今まで手作業で5〜10分かかっていた操作が完全に自動化されました。Claude Code のおかげで、Azure ADの認証まわりのコードを一から書かずに済んだのが特に大きな時間節約でした。
会議作成だけでなく、「参加者の予定を確認してから最適な時間に会議を設定する」という部分も自動化したいと思い、Claude Code に依頼しました。
複数の参加者の空き時間を自動で確認し、全員が参加できる時間帯を見つけて
Teams会議を作成するPythonスクリプトを書いてください。
– 翌営業日以降の5営業日以内で最短の空き時間を探す
– 会議時間は60分で想定
– 見つかった候補時間を表示してユーザーが選択できるようにする
– 選択後に自動で会議作成と招待送信まで行う
Graph API の
/findMeetingTimes を使う実装を書きます。このエンドポイントは参加者全員のカレンダーから空き時間を自動計算してくれます。ユーザー選択のインタラクティブ部分は input() で実装します。
#!/usr/bin/env python3
# find_and_schedule.py
# Claude Code が生成:空き時間確認&自動招待スクリプト
import os
import logging
from datetime import datetime, timedelta
from typing import List
import requests
from msal import ConfidentialClientApplication
logger = logging.getLogger(__name__)
def find_meeting_times(
organizer_id: str,
attendees: List[str],
duration_hours: int = 1,
search_days: int = 5,
) -> List[dict]:
"""Graph API findMeetingTimes で空き時間候補を取得"""
token = get_access_token()
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
# 翌営業日から検索開始
start = datetime.now() + timedelta(days=1)
end = start + timedelta(days=search_days)
attendee_list = [
{"emailAddress": {"address": a}, "type": "required"}
for a in attendees
]
payload = {
"attendees": attendee_list,
"timeConstraint": {
"activityDomain": "work",
"timeSlots": [{
"start": {"dateTime": start.strftime("%Y-%m-%dT09:00:00"), "timeZone": "Asia/Tokyo"},
"end": {"dateTime": end.strftime("%Y-%m-%dT18:00:00"), "timeZone": "Asia/Tokyo"},
}]
},
"meetingDuration": f"PT{duration_hours}H",
"maxCandidates": 5,
"isOrganizerOptional": False,
"returnSuggestionReasons": True,
}
url = f"https://graph.microsoft.com/v1.0/users/{organizer_id}/findMeetingTimes"
resp = requests.post(url, headers=headers, json=payload, timeout=30)
resp.raise_for_status()
suggestions = resp.json().get("meetingTimeSuggestions", [])
logger.info(f"空き時間候補 {len(suggestions)} 件を取得")
return suggestions
def interactive_schedule(organizer_id: str, attendees: List[str], meeting_title: str):
"""空き時間を提示してユーザー選択後に会議を自動作成"""
suggestions = find_meeting_times(organizer_id, attendees)
if not suggestions:
print("😭 5営業日以内に全員参加可能な時間が見つかりませんでした。")
return
print("\n📅 空き時間候補(全員参加可能):\n")
for i, s in enumerate(suggestions, 1):
slot = s["meetingTimeSlot"]
score = s.get("confidence", 0)
start_str = slot["start"]["dateTime"][:16].replace("T", " ")
end_str = slot["end"]["dateTime"][11:16]
print(f" {i}. {start_str} 〜 {end_str} [参加可能率: {score:.0f}%]")
choice = int(input("\n番号を入力して会議を確定してください: ")) - 1
chosen = suggestions[choice]["meetingTimeSlot"]
start_dt = datetime.fromisoformat(chosen["start"]["dateTime"])
end_dt = datetime.fromisoformat(chosen["end"]["dateTime"])
# create_teams_meeting は シナリオ1のモジュールから import
from create_teams_meeting import create_teams_meeting
event = create_teams_meeting(organizer_id, meeting_title, start_dt, end_dt, attendees)
print(f"\n✅ 会議を作成しました!")
print(f" タイトル : {event['subject']}")
print(f" 日時 : {start_dt.strftime('%Y年%m月%d日 %H:%M')} 〜 {end_dt.strftime('%H:%M')}")
print(f" 参加リンク: {event['onlineMeeting']['joinUrl']}")
if __name__ == "__main__":
interactive_schedule(
organizer_id="me@aurant-technologies.com",
attendees=["tanaka@aurant-technologies.com", "kato@aurant-technologies.com"],
meeting_title="プロジェクト進捗確認MTG",
)
📅 空き時間候補(全員参加可能):
1. 2026-04-28 10:00 〜 11:00 [参加可能率: 100%]
2. 2026-04-28 14:00 〜 15:00 [参加可能率: 100%]
3. 2026-04-29 09:00 〜 10:00 [参加可能率: 100%]
4. 2026-04-29 15:00 〜 16:00 [参加可能率: 100%]
5. 2026-04-30 11:00 〜 12:00 [参加可能率: 100%]
番号を入力して会議を確定してください: 1
✅ 会議を作成しました!
タイトル : プロジェクト進捗確認MTG
日時 : 2026年04月28日 10:00 〜 11:00
参加リンク: https://teams.microsoft.com/l/meetup-join/19%3Ameeting_Yzk…
全員の予定を確認してから自動で会議を設定するまで、わずか30秒で完了しました。Claude Code がGraph APIの findMeetingTimes という便利なエンドポイントの存在を教えてくれたおかげで、自分でドキュメントを読み漁る手間が一切かかりませんでした。これだけでも Claude Code への依頼は十分元が取れています。
会議の作成・招待が自動化できたら、次は会議後の処理です。最も時間がかかっていた「議事録作成」を Claude Code に任せました。
Claude APIを使って日本語の議事録(要約・決定事項・アクションアイテム)を
自動生成するPythonスクリプトを書いてください。
要件:
– 会議IDと主催者IDを引数に受け取る
– Graph APIでVTT形式のトランスクリプトを取得
– VTTをパースして話者付きのテキストに変換
– Claude APIで議事録をJSON形式で生成(要約・決定事項・アクションアイテム・次回予定)
– 生成した議事録をMarkdownファイルとして保存
– エラー時は詳細なエラーメッセージを表示
了解です。Graph API からのトランスクリプト取得 → VTT パース → Claude API での議事録生成 → Markdown 保存の一連の処理を実装します。claude-sonnet-4-6 モデルを使用します。
#!/usr/bin/env python3
# generate_minutes.py
# Claude Code が生成:トランスクリプト取得→Claude APIで議事録自動生成
import os, json, re, logging
from datetime import datetime
from pathlib import Path
import requests
import anthropic
from msal import ConfidentialClientApplication
logger = logging.getLogger(__name__)
# Claude Code が使うAnthropicクライアント初期化
claude = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
def get_transcript_content(organizer_id: str, meeting_id: str) -> str:
"""Graph API からVTTトランスクリプトを取得してテキスト変換"""
token = get_access_token()
headers = {"Authorization": f"Bearer {token}"}
# トランスクリプト一覧を取得
list_url = (
f"https://graph.microsoft.com/v1.0/users/{organizer_id}"
f"/onlineMeetings/{meeting_id}/transcripts"
)
resp = requests.get(list_url, headers=headers, timeout=30)
resp.raise_for_status()
transcripts = resp.json().get("value", [])
if not transcripts:
raise ValueError("この会議にはトランスクリプトがありません(Teams Premium が必要な場合あり)")
# 最新のトランスクリプトを取得(VTT形式)
latest_id = transcripts[-1]["id"]
content_url = (
f"https://graph.microsoft.com/v1.0/users/{organizer_id}"
f"/onlineMeetings/{meeting_id}/transcripts/{latest_id}/content"
)
vtt_resp = requests.get(
content_url, headers={**headers, "Accept": "text/vtt"}, timeout=60
)
vtt_resp.raise_for_status()
return parse_vtt(vtt_resp.text)
def parse_vtt(vtt: str) -> str:
"""VTTファイルを話者付きテキストに変換"""
lines = vtt.split("\n")
result, current_speaker = [], ""
for line in lines:
m = re.match(r"^<v ([^>]+)>(.+)$", line.strip())
if m:
speaker, text = m.group(1), m.group(2)
if speaker != current_speaker:
result.append(f"\n【{speaker}】{text}")
current_speaker = speaker
else:
result.append(f" {text}")
return "".join(result).strip()
def generate_minutes(transcript: str, meeting_title: str, attendees: list) -> dict:
"""Claude APIを呼び出して議事録をJSON形式で生成"""
logger.info("Claude API で議事録生成中...")
prompt = f"""以下はMicrosoft Teams会議「{meeting_title}」のトランスクリプトです。
参加者: {", ".join(attendees)}
## トランスクリプト
{transcript[:6000]}
## 出力指示
以下のJSON形式で日本語の議事録を生成してください。
```json
{{
"meeting_title": "会議タイトル",
"date": "YYYY-MM-DD",
"attendees": ["参加者リスト"],
"summary": "会議全体の要約(200字以内)",
"decisions": ["決定事項1", "決定事項2"],
"action_items": [
{{"task": "タスク内容", "owner": "担当者", "due": "YYYY-MM-DD", "priority": "高/中/低"}}
],
"next_meeting": "次回会議の予定",
"notes": "その他特記事項"
}}
```"""
response = claude.messages.create(
model="claude-sonnet-4-6",
max_tokens=4096,
messages=[{"role": "user", "content": prompt}]
)
raw = response.content[0].text
json_match = re.search(r"```json\s*([\s\S]+?)\s*```", raw)
minutes = json.loads(json_match.group(1) if json_match else raw)
logger.info("議事録生成完了")
return minutes
def save_as_markdown(minutes: dict) -> Path:
"""議事録をMarkdownファイルに保存"""
output_dir = Path("./minutes")
output_dir.mkdir(exist_ok=True)
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
path = output_dir / f"minutes_{ts}.md"
actions_md = "\n".join([
f"- [ ] [{a['priority']}] {a['task']} — **{a['owner']}**(期限: {a['due']})"
for a in minutes.get("action_items", [])
])
decisions_md = "\n".join([f"- {d}" for d in minutes.get("decisions", [])])
content = f"""# {minutes['meeting_title']} 議事録
**日時:** {minutes['date']}
**参加者:** {', '.join(minutes['attendees'])}
**生成:** Claude Code (claude-sonnet-4-6)
---
## 要約
{minutes['summary']}
## 決定事項
{decisions_md}
## アクションアイテム
{actions_md}
## 次回会議
{minutes.get('next_meeting', '未定')}
## 特記事項
{minutes.get('notes', 'なし')}
"""
path.write_text(content, encoding="utf-8")
logger.info(f"議事録保存: {path}")
return path
# 2026年5月 月次レビュー 議事録
**日時:** 2026-05-10
**参加者:** 田中 健一, 鈴木 美咲, 山田 拓也
**生成:** Claude Code (claude-sonnet-4-6)
—
## 要約
5月の月次レビューでは、Q2売上が目標の108%達成を確認。
新機能リリースのスケジュール調整と、サポート体制の強化方針が決定された。
## 決定事項
– 新機能βリリース日を5月20日に確定(田中承認)
– カスタマーサポートを1名増員(6月1日入社予定)
– 月次レビューの開催時間を10時→9時に変更
## アクションアイテム
– [ ] [高] 新機能βリリース告知メール作成 — **鈴木 美咲**(期限: 2026-05-15)
– [ ] [高] 採用候補者への最終連絡 — **山田 拓也**(期限: 2026-05-12)
– [ ] [中] 次回レビューのアジェンダ作成 — **田中 健一**(期限: 2026-05-25)
Claude Code のおかげで、60分の会議のトランスクリプトが3分でMarkdown議事録に変換されました。特に感動したのが「アクションアイテムの担当者と期限が自動で抽出される」点です。これまで議事録から手動で追いかけていた作業が完全に不要になりました。
議事録が生成できたら、それをTeamsチャンネルに自動で共有する仕組みも Claude Code に作ってもらいました。
Adaptive Card形式で自動投稿するPythonコードを書いてください。
Adaptive Cardには以下を含めてください:
– 会議タイトルと日時(ヘッダー部分)
– 要約テキスト
– 決定事項リスト(チェックアイコン付き)
– アクションアイテム(担当者・期限・優先度付き)
– 次回会議情報
– Teams Incoming Webhookを使ってPOSTする
– リトライ処理(最大3回)も含めてください
Adaptive Card は JSON 構造が複雑ですが、議事録データを綺麗に表示できる形で実装します。リトライはexponential backoffで実装します。
Claude Codeで業務自動化を始めませんか?
Aurant TechnologiesはClaude Code導入から自動化設計まで無料相談を承っています。