education-cloud-academic-calendar-generate について
カレンダー文書や自然言語の情報から、Education Cloud 組織向けの学年度・学期・セッションレコードを作成する Agent スキルです。日付を検証し、学期の重複を確認したうえで、作成前に承認を求めます。
主な機能
- 01文書やテキストから日付を抽出
- 02学年度・学期・セッションの作成
- 03日付範囲と親子関係の検証
- 04重複学期の確認と承認依頼
活用シーン
- 01学期制・四半期制の年間カレンダー整理
- 02カレンダーから学期と登録日を抽出
- 03学年度・学期・セッションの作成と関連付け
MCP 対応
Salesforce REST API を介して Education Cloud のレコードを操作し、最初に利用可能な API の通信経路を確認します。特定の MCP サーバー機能への依存は記載されていません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill education-cloud-academic-calendar-generateインストール後、Claude Code で/education-cloud-academic-calendar-generateを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
examples/quarter-calendar.jsonsample-output.mdsemester-calendar.jsonreferences/calendar-systems.mddate-validation.mdfoundation_prerequisites.mdscripts/validate_calendar_dates.py詳細
Education Cloud の学期カレンダー生成
Education Cloud 組織向けに基盤となる学期カレンダーのインフラストラクチャを構築します。ドキュメントや自然言語からカレンダーデータを解析し、適切な日付範囲と組織内の関連付けを持つ Academic Year、Term、Session レコードを検証して生成します。
対象範囲
- 対象:Academic Year レコード、Academic Term レコード(セメスター制/クォーター制/トライメスター制)、Academic Session レコード(学期の下位区分)、登録受付期間、履修追加・削除の期限、日付の検証
- 対象外:コースのスケジュール設定(コース提供のスキルに委任)、個々のコースセクションの日付、学資援助の期限、試験日程、教育機関の階層設定(現時点で専用のスキルがないため、Accounts/キャンパスを手動で設定してください)
実行モデル
Salesforce のすべての操作では、相対パスの REST 呼び出しを使用します。Salesforce へのアクセスに CLI やシェルコマンド(sf、sfdx、curl)を使用しないでください。実行環境にこれらが用意されていない場合があります。これは、スキル独自のローカル検証スクリプト(scripts/validate_calendar_dates.py、手順 4 を参照)には適用されません。このスクリプトはローカルで実行され、組織にはアクセスしません。
- レコードの CRUD:sObject REST —
POST /services/data/<ver>/sobjects/<Object>、GET /services/data/<ver>/sobjects/<Object>/<Id>、PATCH /services/data/<ver>/sobjects/<Object>/<Id> - スキーマの確認:sObject describe —
GET /services/data/<ver>/sobjects/<Object>/describe(書き込みの前に、オブジェクトとフィールドの API 名が存在することを確認してください)。 - SOQL
/queryはルーティング可能です:GET /services/data/<ver>/queryに対し、SOQL は URL に?q=...として直接含めず、構造化されたqueryParams: {"q": "..."}として渡してください。実環境でのテストにより、headless-360 のディスパッチ経由で?q=を直接指定すると 404(ROUTE_NOT_FOUND/NOT_FOUND)になる一方、queryParamsなら安定して 200 が返ることが確認されています。既存の term/session をリスト形式で検索する場合に使用するか、作成時に取得した実行時の Id を使ってレコードを特定し、直接GET .../sobjects/<Object>/<Id>で読み戻してください。どちらの方法でも有効です。 - すべての ID は実行時の変数です:作成レスポンスの
body.idから取得してください。Salesforce の Id をリテラルでハードコードしないでください。 - API バージョンは動的に解決してください:「実行時のトランスポート」を参照してください。バージョンをハードコードしないでください。
必須入力
作業を進める前に、以下を収集または推定してください。
- カレンダーシステム:セメスター制(Fall/Spring/Summer)、クォーター制(Fall/Winter/Spring/Summer)、またはトライメスター制
- 学年度の期間:学年度全体の開始日と終了日
- 学期の日付:各学期の授業開始日/終了日、登録受付開始日、履修追加・削除の期限
- Session の下位区分:学期内に複数の Session がある場合(例:Summer Session 1 と 2)
ユーザーがカレンダーのドキュメント(PDF、CSV、スプレッドシート)を提供した場合は、解析してすべての日付を抽出してください。ユーザーが自然言語で情報を提供した場合は、作業を進める前に具体的な日付を確認してください。
注:AcademicYear、AcademicTerm、AcademicSession には Account 参照フィールドがありません。カレンダーレコードは Business Account に関連付けられません。関連付けは Year → Term(AcademicYearId)および Term → Session(AcademicTermId)のみです。
特に指定がない場合のデフォルト:
- カレンダーシステム:セメスター制(Fall、Spring、Summer)
- 学年度の命名形式:「Academic Year YYYY-YYYY」
- 登録受付期間:学期開始の 3~4 か月前に開始
- 履修追加・削除の期限:学期開始から 2 週間後
実行トランスポート
- API バージョンを解決する:headless-360 のディスパッチ経由で
GET /services/data/(バージョン指定なし)を呼び出します。レスポンスは、組織の現在の最大バージョンに解決済みのエンドポイントパスを格納したマップです(例:"limits": "/services/data/v69.0/limits")。versionフィールドを持つ配列ではありません。返されたパスからバージョンを抽出し(正規表現v\d+\.\d+)、以下のすべての呼び出しで<ver>として使用します。 - トランスポートの稼働状態を確認する:headless-360 のディスパッチ経由で
GET /services/data/<ver>/limitsを呼び出します。2xx の場合は、すべての呼び出しをこのトランスポート経由で行います。利用できない場合、または4xx/5xx の場合は、同じ読み取り操作で他に利用可能なトランスポート(別の Salesforce MCP、認証済みのsfCLI、カスタム MCP)を確認し、最初に稼働が確認できたものを使用して、その旨を伝えます。稼働しているものがない場合は処理を中止し、ユーザーに接続を依頼します。決して捏造しないでください。
ワークフロー
すべての手順を順番に実行します。飛ばしたり、順序を入れ替えたりしないでください。進行できない場合は中止し、不足している情報を尋ねてください。
-
基盤の前提条件を確認する —
references/foundation_prerequisites.mdに従い、組織のエディション、Lightning Experience、Education Cloud ライセンス、実行ユーザーの EDU アクセス、Education Cloud Foundation が有効になっていることを確認します(無効の場合は、確認を得てから有効化することで自己修復します)。すべての確認が完了するまで、ステップ1に進まないでください。 -
カレンダーシステムを確認する
- ユーザーに、学期制、クォーター制、トライメスター制のいずれかを尋ねます。
- システムの種類に応じて、想定されるターム名を特定します。
-
カレンダーデータを収集する
- ユーザーが文書(PDF、CSV、スプレッドシート)を提供した場合:解析して、年度、ターム、セッションの日付を抽出します。
- ユーザーが自然言語で説明した場合:説明から日付を抽出します。
- 詳細が不十分な場合:具体的な日付(タームの開始日/終了日、登録期間、締切日)を尋ねます。
-
オブジェクトと項目のスキーマを確認する
- 書き込みの前に、使用する各オブジェクトと設定するすべての項目が組織に存在することを確認します。
GET /services/data/<ver>/sobjects/AcademicYear/describe、.../AcademicTerm/describe、.../AcademicSession/describe fields配列を読み込み、使用する項目の API 名(Name、AcademicYearId、StartDate、EndDate、RegistrationOpenDate、RegistrationCloseDate、Season、AcademicTermId、ClassStartDate、ClassEndDate、AddDropDeadline)と照合します。- 選択リスト項目
Season(AcademicTerm.Season、値はFall/Spring/Summer/Winter)に注意してください。この項目のpicklistValuesに存在する値のみ送信します。AcademicSession.Typeは別の選択リスト(Semester/Trimester/Quarterなど)で、このワークフローでは使用しません。Seasonと混同しないでください。 - 項目が存在しない場合:処理を中止します。未確認の項目名を使って POST しないでください。不一致をユーザーに報告します。
- 書き込みの前に、使用する各オブジェクトと設定するすべての項目が組織に存在することを確認します。
-
日付範囲を検証する
- 収集した年度/ターム/セッションのデータを、
scripts/validate_calendar_dates.pyの先頭に記載されている JSON 形式にまとめてから、一度だけ実行します。python3 scripts/validate_calendar_dates.py <<'EOF' {"academicYear": {...}, "terms": [...], "sessions": [...]} EOF - このスクリプトは、タームの終了日が開始日より後であること、年度がすべてのタームを含んでいること、登録開始日がターム開始日より前であること、履修追加/取消の締切日がタームの範囲内であること、セッションが親タームの範囲内にあることを確認し、各タームの
RegistrationCloseDateを計算します。 validがfalseの場合:処理を中止し、errorsの各項目をユーザーに報告して、修正した日付を尋ねます。ステップ5に進まないでください。warnings(意図的なセッションの重複など)は処理を妨げません。各項目をユーザーに提示し、ステップ6で確認を求めます。computedRegistrationCloseDatesを引き継ぎます。ステップ8では再計算せず、これを使用します。
- 収集した年度/ターム/セッションのデータを、
-
重複する用語を検出する
- AcademicTerm の
Nameには一意制約がないため、名前が重複していても挿入時にエラーにはなりません。作成前にリストをスキャンし、名前が一致する既存の用語がないか確認します。GET /services/data/<ver>/queryを使用し、queryParams: {"q": "SELECT Id, Name, StartDate, EndDate FROM AcademicTerm WHERE Name = 'Fall 2025'"}を指定します。SOQL は URL に?q=としてインラインで記述せず、queryParams経由で渡してください(インラインで記述すると 404 になります)。または、同じセッション中にユーザーがこの年のカレンダーをすでに作成している場合は、再作成せず、そのときに取得した Id を再利用します。 - 名前が重複する用語が見つかった場合は、日付を比較し、既存レコードを更新するかスキップするかをユーザーに確認します。
- 重複チェックの読み取り自体に失敗した場合(2xx 以外、またはトランスポートエラー)は、停止してください。学期の作成に進んではいけません。失敗をユーザーに伝え、どのように進めるか確認します。重複する可能性のあるレコードを、通知せずに作成してはいけません。
- AcademicTerm の
-
提案するカレンダー構成を表示して確認する
- 作成する内容の概要を提示します。Academic Year の名前と日付、各学期の名前・日付・登録期間・計算済みの
RegistrationCloseDate、および親学期ごとにまとめた各セッションの名前と日付を表示します。入力例はexamples/sample-output.mdを参照してください。 - 手順 4 でセッションの重複に関する警告が出た場合は、それぞれについて重複期間を示し、意図した重複かどうかユーザーに確認します。
- ユーザーに「これらのレコードをすべて作成しますか?」と尋ねます。
- 手順 7 に進む前に、ユーザーの確認を待ちます。
- 作成する内容の概要を提示します。Academic Year の名前と日付、各学期の名前・日付・登録期間・計算済みの
-
Academic Year レコードを作成する
- 手順 6 でユーザーが確認した後、Academic Year を挿入します。
POST /services/data/<ver>/sobjects/AcademicYear {"Name": "Academic Year 2025-2026"} - 用語との関連付けに使う
academicYearIdをレスポンスのbody.idから取得します。 - 次のように報告します:「Academic Year を作成しました:Academic Year 2025-2026」
- 手順 6 でユーザーが確認した後、Academic Year を挿入します。
-
Academic Term レコードを作成する
- 各学期(Fall、Spring、Summer、またはそれに相当する Quarter)について、手順 7 の
academicYearIdをAcademicYearIdに設定して挿入します。POST /services/data/<ver>/sobjects/AcademicTerm {"Name": "Fall 2025", "Season": "Fall", "StartDate": "2025-08-18", "EndDate": "2025-12-12", "AcademicYearId": "<academicYearId>", "RegistrationOpenDate": "2025-04-01", "RegistrationCloseDate": "2025-08-17"}RegistrationCloseDateには、手順 4 のcomputedRegistrationCloseDates[<term name>]の値を使用します。手作業で再計算してはいけません。Seasonは制限付き選択リストです。値はFall、Spring、Summer、Winterです。AcademicTerm には add/drop フィールドはありません。add/drop の期限は AcademicSession にのみ設定します。
- セッションとの関連付けに使う各
termIdをレスポンスのbody.idから取得します。 - 進捗を次のように報告します:「3 つの学期を作成しました:Fall 2025、Spring 2026、Summer 2026」
- 各学期(Fall、Spring、Summer、またはそれに相当する Quarter)について、手順 7 の
-
Academic Session レコードを作成する
- 各セッション区分(例:Summer Session 1、Summer Session 2)について、手順 8 の親学期の
termIdをAcademicTermIdに設定して挿入します。POST /services/data/<ver>/sobjects/AcademicSession {"Name": "Summer Session 1", "ClassStartDate": "2026-05-26", "ClassEndDate": "2026-07-03", "AddDropDeadline": "2026-06-11", "AcademicTermId": "<termId>"} - 進捗を次のように報告します:「2 つのセッションを作成しました:Summer Session 1、Summer Session 2」
- 各セッション区分(例:Summer Session 1、Summer Session 2)について、手順 8 の親学期の
-
完了の概要を作成する
- 最終的な概要を表示します。作成した Academic Year とその日付、作成した各 Term の期間、親 Term ごとにまとめた作成済みの各 Session を示し、Year → Term → Session の関連付けについても一文で説明します。入力例は
examples/sample-output.mdを参照してください。 - 手順 4 の警告にあるようにセッションが重複している場合は、重複期間と、その重複が意図したものとして確認されたことを記載します。
- 重複のためにスキップした学期がある場合は、その学期を理由とともに列挙します。
- 最終的な概要を表示します。作成した Academic Year とその日付、作成した各 Term の期間、親 Term ごとにまとめた作成済みの各 Session を示し、Year → Term → Session の関連付けについても一文で説明します。入力例は
オンデマンド検証
ステップ 10 の後はいつでも利用できます。必須のワークフローステップではありません。ユーザーからレコードの抜き取り確認やカレンダー範囲の表示を依頼された場合にのみ使用してください。
- 特定のレコード(例:「Fall 2025 を表示して」):取得済みの Id を使ってコールドリードします。
GET /services/data/<ver>/sobjects/AcademicTerm/<termId>を使用します(すべてのフィールドが返されます。?fields=セレクターは使用しないでください)。Id を取得していない場合は、GET /services/data/<ver>/queryとqueryParams: {"q": "SELECT Id FROM AcademicTerm WHERE Name = 'Fall 2025'"}を使って名前で検索します(SOQL は URL に?q=として埋め込まず、queryParams経由で渡してください。URL に埋め込むと 404 になります)。親階層を含むすべてのフィールド値を表形式で表示します。 - カレンダー表示(例:「Summer 2026 の全日程を表示して」):取得済みの Id を使って、学期と各子セッションをコールドリードします。
GET /services/data/<ver>/sobjects/AcademicTerm/<termId>およびGET /services/data/<ver>/sobjects/AcademicSession/<sessionId>を使用し、表(Record Type、Name、Start Date、End Date、Duration、Registration Open、Registration Close、Add/Drop)として表示します。該当する場合は重複期間を計算して表示します。記入済みの出力例についてはexamples/sample-output.mdを参照してください。
ルール/制約
| 制約 | 理由 |
|---|---|
| 書き込み前に describe でオブジェクトとフィールドの API 名を検証する | 未検証のフィールドへの POST の失敗を防ぎます。Season の選択リスト値は AcademicTerm.Season の picklistValues と一致する必要があります |
| 学期の終了日は開始日より後でなければならない | プラットフォームの検証で無効な日付範囲は拒否されます |
| 登録受付開始日は学期開始日より前でなければならない | 授業開始後に学生が登録できないようにするためです |
| 追加・削除期限(AcademicSession 上)は学期開始日以降でなければならない | 授業開始前の期限は論理的に無効です。AcademicTerm には追加・削除フィールドがありません |
| Academic Year はすべての子学期を含む期間でなければならない | 学期の日付が年度の範囲外にあると、レポートに問題が生じます |
| 同じ学期内でセッションが重複してもかまわない | Summer のセッションでよくあるパターンです(例:Session 1 は 5 月~7 月、Session 2 は 6 月~8 月) |
| 学期名は暦の慣例に合わせる | セメスター制では Fall/Spring/Summer、クォーター制では Fall/Winter/Spring/Summer を使用します |
| API バージョンは動的に解決し、ハードコードしない | Org の最大 API バージョンは時間とともに変わります。古いバージョンをハードコードすると、Org の現在のバージョンとずれていきます |
作成前に SOQL /query で学期の重複を確認し、q は URL に埋め込まず queryParams 経由で渡す | /query は headless-360 経由でルーティングできますが、queryParams を使用した場合に限ります。?q= を埋め込むと 404 になります(実環境でのテストで確認済み)。学期の Name は一意制約がないため、重複していても挿入時にエラーになりません。重複確認の読み取りに失敗した場合は、確認を省略せず作成を中止してください |
注意点
| 問題 | 対応 |
|---|---|
| ユーザーが相対的な日付(「次の Fall」など)を指定する | 挿入前に絶対日付に変換します(例:「次の Fall」→「Fall 2026」とし、具体的な日付を指定) |
| 重複するセッションがエラーとして報告される | ユーザーに確認します。多くの教育機関では重複は意図的です(例:Summer Session 1 と 2 が 1 週間重複) |
| 日付が異なる重複学期 | 既存の学期を更新するか、スキップするかユーザーに尋ねます。確認なしに自動更新しないでください |
| 登録受付期間が学期開始後になっている | 無効です。正しい登録受付開始日をユーザーに尋ねてください |
| Academic Year の終了日が最後の学期の終了日より前になっている | Academic Year をすべての学期を含むように延長するか、学期の日付を調整します |
| 入力に学期単位の追加・削除日が含まれているが、セッションの区分がない | AcademicTerm には追加・削除フィールドがありません。学期とセッションのどちらに配置するかをユーザーに伝えるか、その日付を設定するために学期ごとにセッションを作成する案を提示します。AcademicTerm に書き込んだり、黙って省略したりしないでください |
| ユーザーが 3 部構成の年度範囲(2025-2026-2027)を指定する | Academic Year は正確に 2 年間(例:2025-2026)であることをユーザーに説明し、確認します |
| 親学期のないセッション | すべてのセッションは学期にリンクする必要があります。ユーザーが学期を指定せずにセッションの日付を入力した場合は、どの学期に属するか尋ねてください |
出力の期待値
成果物:
- Academic Year レコード:すべての学期を含む単一の年度レコード(例:「Academic Year 2025-2026」)
- Academic Term レコード:年度ごとに3〜4件の学期レコード(セメスター制の場合は Fall、Spring、Summer、クォーター制の場合は Fall、Winter、Spring、Summer)
- Academic Session レコード:0〜N件のセッションレコード(セメスター制では通常、Summer セッションが2件)
- サマリーレポート:作成したすべてのレコードについて、名前、日付、関連付けを一覧表示する表
学期は親の Academic Year(AcademicYearId)にリンクし、セッションは親の Academic Term(AcademicTermId)にリンクします。これらのオブジェクトには Account 参照フィールドがありません。
スキル間の連携
| 必要なもの | 委任先 |
|---|---|
| 組織階層(Accounts、キャンパス) | 専用スキルはまだありません — 手動で設定します |
| コースのスケジュール設定と開講 | コース開講生成スキル |
参照ファイルの一覧
| ファイル | 読み込むタイミング |
|---|---|
references/foundation_prerequisites.md | ステップ0 — 組織のエディション、Lightning Experience、EDU ライセンス、権限セットの割り当て、Education Cloud Foundation の有効化。スタンドアロンで実行するたびに、ステップ1の前に実行します |
references/calendar-systems.md | ユーザーのカレンダーシステムが不明な場合、または標準的でない場合 |
references/date-validation.md | 複雑な日付の重複や複数セッションの学期を検証する場合 |
examples/semester-calendar.json | セメスター制の出力構造を確認する場合 |
examples/quarter-calendar.json | クォーター制の出力構造を確認する場合 |
examples/sample-output.md | 提案構造の表示、完了サマリー、カレンダービューの表の記入例を確認する場合 |
scripts/validate_calendar_dates.py | ステップ4で実行し、日付範囲を検証して RegistrationCloseDate を決定論的に算出します |