ブログ一覧に戻る
新機能

ガントチャートの計算をAIに任せない設計|MCPでサーバー側CPMを返すGantyの新機能

Ganty Team

ClaudeをはじめとするAIエージェントから「クリティカルパスを教えて」と聞いて、間違った答えを返されたことはありませんか? それはAIの能力不足ではなく、そもそもLLMに計算をさせる設計が悪いからです。Gantyは2026年6月、サーバー側で確定計算してJSONで返す2つのMCPツール — get_critical_pathreschedule_and_propagate — をリリースしました。本記事は、その設計判断と実装の解説です。

問題: LLMにタスク一覧を読ませてCPMを推論させる時代の限界

典型的なシナリオ: Claudeに「プロジェクトのタスク一覧」を読ませる(list_tasks) → 「クリティカルパスを教えて」と頼む → LLMが推論して答えを返す。これで実用に耐える答えが返ることは少ないです。なぜか:

  • 依存関係グラフを正しく辿らない: 並行経路の合流で「遅い方に合わせる」処理を見落とす
  • 進捗反映が雑: progress=100のタスクを動かさない/progress=50の残り作業を半分にする等の細かい挙動が安定しない
  • lag days を反映し忘れる: 依存関係の遅延日数を考慮しない場合がある
  • 循環依存で無限ループ: 入力データに循環があるとLLMは延々と推論を続ける
  • 答えが毎回違う: 同じ入力でも温度の影響で結果がブレる

これらは「LLMの能力を上げれば直る」種類の問題ではありません。決定的アルゴリズム(forward/backward pass)が必要な計算を、確率的生成モデルにやらせているのが根本原因です。

解決: 計算はサーバーで、LLMは仲介者に

Gantyのアプローチは単純です。クリティカルパス計算をTypeScriptでサーバー側に実装し、MCPツールとして公開する。LLMはツール呼び出しと結果の自然言語化だけを担当する。

// Claude側の見え方(疑似コード)
ユーザー: 「クリティカルパスを教えて」
Claude: get_critical_path(project_id: "...") を呼ぶ
Ganty: {
  critical_path: [
    { task_id: "...", name: "要件定義", start: "2026-06-01", end: "2026-06-05" },
    { task_id: "...", name: "API設計", start: "2026-06-05", end: "2026-06-12" },
    ...
  ],
  project_end_date: "2026-08-15",
  total_duration_days: 75,
  ...
}
Claude: 「クリティカルパスは『要件定義 → API設計 → ...』の経路です。
        プロジェクト完了は8月15日、総期間75日です。」

LLMは数値を推論しません。サーバーが計算した確定値をそのまま読み上げます。間違いようがないのがこの設計の核心です。

get_critical_path: 進捗反映の残り作業ベースCPM

get_critical_path は標準的なCPMアルゴリズムを実装しています。

  1. 循環依存検出: 計算前に反復DFSで循環を検出。あれば error: 'cyclic_dependency' と循環パスを返してハングを防ぐ
  2. Forward pass: トポロジカル順で各タスクの earliest start (ES) と earliest finish (EF) を計算
  3. Backward pass: 逆順で latest start (LS) と latest finish (LF) を計算
  4. Slack計算: slack = LS - ES。slackが0のタスクがクリティカル
  5. クリティカルパス抽出: slack=0のタスクをトポロジカル順に並べて返す

進捗の扱いがポイントです。progress=100 のタスクは「事実上のpin」として動かさず原状で固定。それ以外は 残り期間 = round(全期間 × (1 - progress/100)) で計算します。as_of_date パラメータで「今日」を指定でき、未着手タスクのES下限を max(as_of_date, original_start) とすることで現実的な見通しを返します。

reschedule_and_propagate: 影響予測 → 安全な適用

「あるタスクをずらしたら何が起きる?」は、PMにとって最も頻度の高い問いの1つです。reschedule_and_propagate はこれを2段階で解決します。

  • Dry-run(デフォルト): DBには一切触らず、計算結果(各タスクのbefore/after・プロジェクト完了日の差・pinへの抵触conflicts)をJSONで返す
  • Commit: 同じ計算結果を Prisma $transaction で一括適用。conflictsが1件でもあれば書き込まずに返す(全件成功 or 全件ロールバック)

伝播ルール:

  • 押し出しのみ(no-pull cascade): 後続タスクは「先行のnew_end + lag」が現在より遅ければ押し出される。早ければ動かさない(安全策)
  • 合流はmax: 複数先行を持つタスクは「最も遅く終わる先行」に合わせる
  • pin検出で停止: progress=100 or pinned_task_ids で指定されたタスクに押し出しが届いたら、conflict配列に記録してその先には伝播しない

この設計により、「3日ずらしたらリリースが何日遅れる?」をAIに聞いて、Claudeから「リリースは5日遅れます。ただし固定タスクの〇〇に2日の抵触があります」と返ってくる体験が実現します。納得すれば mode: 'commit' で実行、納得しなければ計画を見直し。

設計原則: 正しく計算できないケースは未対応で返す

本機能の価値は「正しい答えを返すこと」に100%依存します。だからこそ「計算できないケースは間違った答えを出すより明示的に未対応として返す」を全体の鉄則にしました。

  • 循環依存error: 'cyclic_dependency' + 循環パス
  • 未対応の依存タイプ(SS/FF/SF) → スキーマ上FSのみだが、limitations配列で明記
  • 祝日 → 祝日テーブル無し、土日のみ非稼働(business_days=true時)。limitations配列で明記
  • extraSegments(複数期間タスク) → v1で計算上は無視。limitations配列で明記
  • リソース別カレンダー → v1未対応。limitations配列で明記

各レスポンスに常時含まれる limitations 配列をClaudeが認識して、ユーザーに「この計算は祝日を考慮していません」のような注釈を付けることができます。

テスト戦略: 「動いた」ではなく「正しい数字が出た」を検証

計算の正しさが価値の全てなので、ゴールデンテストを28個実装しました。すべて「期待する具体的な日付・日数」をアサートしています。

  • 線形チェーン A→B→C で +3d シフト → C完了が+3d、project_end +3d
  • ダイヤ型 A→B,A→C,B→D,C→D で「遅い方に合わせる」
  • 循環 A→B→A → cyclic_dependency 即返し、タイムアウト2秒以内
  • progress=100 タスクへの押し出し → conflicts記録、commit時にDB不変
  • progress=50 タスクの残り期間が半分
  • 手計算済みの6タスクCPMで critical path と全タスクのslackが完全一致
  • business_days=true で土日スキップ
  • lagDays が forward pass / propagation 両方で正しく反映
  • respect_dependencies=false で直接タスクのみ動く
  • 負シフトで後続を引っ張らない(no-pull)

tsxで node --test を回す軽量構成。28テストが0.3秒で全パスする状態を保っています。

使い方

Ganty のMCP連携は全プラン無料です。MCP連携ガイドから5分でセットアップして、まずは get_critical_path を呼んでみてください。設定で詰まったらMCPセットアップで詰まる9つの問題と解決策を参照。実例はMCP活用 7実例に追加した実例6・7をご覧ください。

関連

関連記事