Why We Don't Let AI Calculate Critical Paths: Server-Side CPM in Ganty's MCP Tools
Ganty Team
Have you ever asked Claude "what's the critical path for this project?" and gotten a confidently wrong answer? That's not the model's fault — it's a design problem. In June 2026 Ganty shipped two MCP tools — get_critical_path and reschedule_and_propagate — that compute the answer on the server and return JSON. This is the architecture deep-dive on why we chose that path.
The Problem: Asking the LLM to Compute CPM
The naïve flow: pass the LLM the task list (via list_tasks), ask "what's the critical path," let the model derive the answer. It rarely works in practice. Why?
- The graph walk is sloppy. At a merge point, "take the later finisher" is often missed.
- Progress handling is unstable. progress=100 should stay put; progress=50 should halve remaining work. Models forget.
- Lag days get dropped. Dependency lag isn't always factored into the date math.
- Cycles hang. With a cyclic dependency, an LLM may loop indefinitely trying to make the math close.
- Non-determinism. Same input, slightly different temperature, different answer.
These aren't "wait for a bigger model" problems. They're using a stochastic generator for what needs a deterministic algorithm (forward/backward pass).
The Fix: Server Computes, LLM Relays
Ganty's approach: implement CPM in TypeScript on the server, expose it as an MCP tool. The LLM picks the tool, then verbalizes the result. It doesn't do math.
// What Claude sees (pseudocode)
User: "show me the critical path"
Claude: invokes get_critical_path(project_id: "...")
Ganty: {
critical_path: [
{ task_id: "...", name: "Requirements", start: "2026-06-01", end: "2026-06-05" },
{ task_id: "...", name: "API design", start: "2026-06-05", end: "2026-06-12" },
...
],
project_end_date: "2026-08-15",
total_duration_days: 75,
...
}
Claude: "The critical path is Requirements → API design → ...
The project ends on August 15, total 75 days."
The model isn't deriving numbers; it's relaying server-computed values. There's no room to be wrong.
get_critical_path: Progress-Aware Forward/Backward Pass
Standard CPM, implemented in five steps:
- Cycle detection first. Iterative DFS; on cycle, return
error: 'cyclic_dependency'with the cycle path. No hangs. - Forward pass. Compute earliest start (ES) and earliest finish (EF) in topological order.
- Backward pass. Compute latest start (LS) and latest finish (LF) in reverse.
- Slack = LS − ES. Slack-zero tasks are critical.
- Critical path = slack-zero tasks in topological order.
The progress model: progress=100 tasks are pinned and stay fixed. Others use remaining = round(full_duration × (1 - progress/100)). An optional as_of_date floors ES at max(as_of_date, original_start), projecting overdue work forward realistically.
reschedule_and_propagate: Simulate, Then Apply
"What if I shift this task?" is the PM question of the day. reschedule_and_propagate answers it in two modes:
- Dry-run (default): no DB writes. Returns each task's before/after, project end delta, and any pin conflicts.
- Commit: same calculation wrapped in a Prisma
$transaction. If any conflict exists, nothing is written — all-or-nothing.
Propagation rules:
- Push-only cascade. A successor moves only if predecessor.new_end + lag > successor.current_start. Earlier shifts don't pull successors forward (conservative).
- Merges take the max. A successor with multiple predecessors lines up with the latest finisher.
- Pin stops propagation. progress=100 tasks (and anything listed in pinned_task_ids) record a conflict instead of moving. Cascade stops there.
The UX: ask "how much does release slip if I push task X by 3 days?" Claude returns "release slips 5 days, but there's a 2-day conflict against pinned task Y." Approve it → mode: 'commit'. Don't approve it → revise plan.
Design Principle: When We Can't Be Right, We Say So
Every response includes a limitations array that names what the calculation did and did NOT account for:
- Cycles →
error: 'cyclic_dependency'+ cycle path - Other dependency types (SS/FF/SF) → Ganty's schema is FS-only; documented in limitations
- Holidays → no holiday table; only Sat/Sun skipped when business_days=true. Documented.
- Multi-period tasks (extraSegments) → ignored in v1. Documented.
- Resource calendars → not supported in v1. Documented.
Claude reads the limitations array and can warn the user accordingly ("this calculation doesn't account for national holidays").
Test Strategy: Assert Specific Numbers, Not "It Ran"
Correctness is the entire value here, so we shipped 28 golden tests asserting concrete expected dates and day counts:
- Linear A→B→C, shift A by +3d → C ends +3d, project_end +3d
- Diamond A→B, A→C, B→D, C→D → D follows the later finisher
- Cycle A→B→A → cyclic_dependency returned; completes within 2 seconds
- Push into a progress=100 task → conflicts recorded; commit leaves DB unchanged
- progress=50 task halves remaining duration
- Six-task hand-calculated CPM → critical path and per-task slack match exactly
- business_days=true skips Sat/Sun
- lagDays respected in both forward pass and propagation
- respect_dependencies=false moves only the named task
- Negative shifts don't pull successors (no-pull)
Tests use tsx + Node's built-in node:test. 28 tests pass in under 0.3 seconds.
Try It
MCP integration is free on every plan. Set up in 5 minutes via the MCP integration guide and call get_critical_path from Claude. If setup hangs, check the 9 common MCP setup problems guide. For examples, see Examples 6 and 7 in our updated 7 MCP automation examples.
Related
- MCP integration guide
- 7 MCP automation examples (updated with the new tools)
- How to use Claude Code for project management
- Best AI Gantt chart tools 2026
- MCP integration overview (v1 limitations documented)
Related Articles
Ganty Now Supports MCP: Drive Your Gantt Charts Directly From Claude
Ganty now supports the Model Context Protocol (MCP). Read and write Gantt chart tasks from Claude Desktop, Claude Code, and Cursor in natural language. Free on all plans, three-minute setup.
2026-06-01How to Use Claude Code for Project Management: 5 MCP Examples for Natural-Language Gantt Workflows
A complete guide to operating Gantt charts from Claude Code in natural language. Setup, plus five developer-focused MCP patterns: auto-task from PRs, sprint progress summaries, release checklists, post-review delay surfacing, and code-aware task generation.
2026-06-019 Common MCP Setup Problems and How to Fix Them (Claude Desktop & Claude Code)
Complete MCP troubleshooting guide. Covers token/auth errors, config file JSON issues, port conflicts, SSL certificates, rate limits, and version mismatches — for both Claude Desktop and Claude Code, with a diagnostic flow.