
Cpm Planner
io.github.praxecv0.2.1Updated Oct 11, 2026
Critical Path Method scheduling and lock-aware parallel cohorts for MCP agents.
Overview
An MCP server that schedules agent task graphs with Critical Path Method and leases lock-aware parallel work cohorts.
- What it does
- It accepts a task graph and computes earliest and latest start and finish, slack, the critical path and bottleneck tasks. It then leases lock-aware cohorts of ready deliverables with disjoint file sets so several workers can run in parallel without colliding. Read-only tools lint plans, level them against resource capacities, run Monte Carlo risk, and track earned value (PV, EV, AC, SPI, CPI) against a frozen baseline. An optional AI review proposes verified schedule improvements.
- When to use it
- Use it when an assistant or orchestrator must plan and coordinate multi-step work with dependencies, parallel workers and shared files. It suits teams that want plan-as-code files in the repository, resource leveling, risk simulation or earned-value tracking. It is not needed for simple single-step tasks.
- Requirements
- Runs as a local process over stdio. Install a prebuilt binary, the npm launcher (Node 18 or later), a Cargo build (Rust 1.99 or newer), or the Docker image. Optional environment variables include CPM_PLANNER_DB for the SQLite state path, CPM_PROJECT_ROOT for plan files, CPM_MAX_TTL_SECS for lease limits, and OPENROUTER_API_KEY or CPM_OPENROUTER_KEY_FILE for the optional plan review.
Installation
In SourceWeft
- Open Cpm Planner in the dashboard and add it to a workspace.
- Enable the server for the chats that should use its tools.
Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.
Other MCP clients
Follow the launch instructions in the repository.
README
cpm-planner
[CI] [Release] [License: Apache-2.0]
cpm-planner is a Critical Path Method (CPM) planner for agent work, exposed as an MCP server. You submit a task graph and it computes the schedule: earliest and latest start and finish, slack, the critical path and the bottleneck tasks that gate completion. It then leases lock-aware cohorts of ready deliverables with disjoint file sets, so several workers can run in parallel without colliding. Plans can live in the repository as plan-as-code files with forkable, comparable variants; read-only tools lint, level against resource capacities and run Monte Carlo risk; earned-value tools track PV, EV, AC, SPI and CPI against a frozen baseline; and an optional AI review proposes verified schedule improvements. Any MCP client (Claude Code, Cursor, VS Code, Codex, a custom orchestrator, or a praxec workflow) drives it over stdio. It is a standalone tool with no dependency on praxec.
Contents
- Install
- Quickstart: plan as code
- Quickstart: earned value
- Use with AI agents
- MCP tools
- Use as a library
- Praxec
- Environment variables
- Documentation
- Contributing
- License
Install
Prebuilt binary (recommended)
Prebuilt packages are published for Linux, macOS and Windows on x86_64 and ARM64.
The installers resolve your OS and CPU and download the matching archive. They
verify its SHA-256 against the release's checksums.sha256 before installing,
then atomically replace the binary in a user-local directory. A checksum mismatch
aborts with a non-zero exit and installs nothing. Re-running the installer upgrades
in place.
The installer prints the absolute path it installed to. Use that path when you register the server with a GUI client.
Installer details: inspecting the script, PATH, upgrades and direct downloads
If you would rather read the script before running it, download, inspect, then run
(see Pin a version for <version>):
PATH. If the install directory is not on your PATH, the installer prints the
exact line to add. Pass --add-to-path (-AddToPath on Windows) to have it do that
for you, user-level only and never with sudo.
On Linux and macOS it appends the line only if it is not already there, to:
~/.zshrcfor zsh;~/.bash_profileif it exists, else~/.bashrc, for bash;~/.profilefor other shells.
For fish it prints fish_add_path <dir> for you to run instead of writing a file.
Downloads. The installers only accept https:// download URLs.
Upgrades. After upgrading, restart your MCP client so it launches the new
binary. On Windows the old exe is renamed to cpm-planner.exe.old and removed on
the next run.
Direct downloads. You can also download an archive yourself and check it
against checksums.sha256; a machine-readable release-manifest.json is published
too. All assets are at https://github.com/praxec/cpm-planner/releases/latest.
Pin a version
In the commands in this README, <version> is a release tag such as v0.2.0. Pick
one from the releases page.
npm
With Node 18 or later, install the cpm-planner command from npm:
The package is a small launcher. It downloads the release binary for your
platform, verifies its checksum and caches it, during install or, where npm
skips install scripts, on first run. The package
version is the release version, so npm install -g @matthew-cochran/[email protected]
pins one. To run it without installing, use npx -y @matthew-cochran/cpm.
For details and environment variables, see npm/README.md.
From source
Build and install a tagged release with Cargo (Rust 1.99 or newer):
The crates.io listing lags behind the GitHub releases until the next
cargo publish, so cargo install cpm-planner does not give you the current
version yet. Install from the tag as above.
Docker
-i keeps stdin open for the stdio transport.
The image sets CPM_PLANNER_DB=/data/cpm-planner.db and declares /data as a
volume owned by the unprivileged app user, so a named volume persists plan state
across runs. For a bind mount (-v "$PWD:/data"), run as yourself so the directory
is writable: --user "$(id -u):$(id -g)". Pass -e CPM_PLANNER_DB=... only to
override the path.
To register the image with a client, put docker in command and the rest in
args. For example, in Claude Code:
Verify the install
It prints cpm-planner <version>. To check the MCP side without a client, run the smoke
script from the repository. It performs the MCP initialize and tools/list handshake and
fails if plan.submit, plan.status or plan.get is missing:
Quickstart: plan as code
Write a plan as .cpm-planner/plans/<name>/<variant>.json (a PlanGraph):
plan.lint {path: ".cpm-planner/plans/checkout/main.json"}— static checks, no state written.plan.sync {path: ".cpm-planner/plans/checkout/main.json"}— register the variant and return itsplan_id.plan.status {plan_id}— schedule, critical path, ready set and locks.
plan.sync tracks the file by content hash; plan.status reports
definition_drift when the tracked file and the stored head graph disagree.
Quickstart: earned value
Freeze the baseline, record progress, then read the report (earned_pct is earned in
proportion under earning_rule: "weighted"; fifty_fifty credits 50% once a deliverable is in progress or has any
reported earned_pct, and the default zero_hundred
earns at completion):
plan.baseline {plan_id}— freeze the CPM schedule and budgets as baseline 1.plan.mark_status {plan_id, deliverable_id, caller_id, status: {"status": "in_progress"}, earned_pct: 50, actual_effort_hours: 4}— report progress and actual cost.plan.ev {plan_id}— PV, EV, AC, SV, CV, SPI, CPI, EAC and alerts.plan.snapshot {plan_id, format: "markdown"}— append the reading and export a Markdown table.
A ratio with a zero denominator is null and explained in undefined; alerts
(SPI_BELOW_0_9, CPI_BELOW_0_9) compare the two latest stored readings.
Use with AI agents
Any MCP client can run cpm-planner over stdio: register the cpm-planner
binary as the command, or, with Node 18 or later, npx -y @matthew-cochran/cpm.
For a GUI client, use the absolute path the installer printed.
Install the agent skills (/cpm-plan and friends) with
cpm-planner skills install --target <tool> --user (or --project <dir>), where
<tool> is claude, codex, cursor, copilot, gemini, all, or
agents-md (project only).
docs/AGENT-INSTALL.md has the per-client steps (Claude
Code, Claude Desktop, Codex, Cursor, VS Code, Gemini CLI and others), env
blocks, verification and troubleshooting. llms.txt indexes the docs
for LLMs.
MCP tools
plan.lint, plan.simulate and plan.review take exactly one of an inline graph, a stored
plan_id, or a plan-file path; plan.schedule takes graph or plan_id;
plan.schedule and plan.simulate reject what plan.submit rejects, and plan.lint reports it as findings. Monte Carlo output is reproducible for a given seed on the same platform
and toolchain; bit-identical results across targets or compiler versions are
not guaranteed, because float math functions can differ.
Plan-as-code workflow
Author a plan as a file at .cpm-planner/plans/<name>/<variant>.json, check it
with plan.lint {path}, register it with plan.sync {path} and execute the
returned plan_id. A named line has many variants but exactly one selected:
plan.fork creates a draft from structured edits, plan.compare scores the
variants, and plan.select makes one executable. Keep definitions in tracked
files — never keep untracked scratch graphs.
Plan review (optional)
plan.review asks Jev (typesafe/jev-1.13), TypeSafe's
calibrated-judgment model, about a lint-clean plan through OpenRouter: one
batched call of at most 64 questions per review. It is off until you set
OPENROUTER_API_KEY (or CPM_OPENROUTER_KEY_FILE); without a key the tool
still answers, with review_unavailable and the lint report. An invalid LLM
setting never stops the server: it is logged at startup and plan.review
reports review_unavailable with a reason naming the setting.
-
Probabilities are advisory. Treat findings as a second opinion. Proposals are verified mechanically (the edited plan must lint clean and simulate to a shorter makespan); apply one with
plan.fork {edits}, thenplan.compare. -
Cost. Each
plan.reviewcall that reaches the judge is one billed OpenRouter request; Jev costs about $0.042 per million input tokens on OpenRouter. Each report carries the provider'susagewhen given. At most 2 reviews that call the judge run at once per server; further calls wait for a slot (reviews that end before the call, such as no key or lint errors, never wait). -
Privacy. This is everything sent to OpenRouter in that one request:
- a fixed task sentence, the plan makespan and the critical path (ids);
- the questions: for each, its kind, the deliverable ids it names, and a
question sentence that quotes those ids and, depending on the kind, the
prerequisite's
consumestext, the deliverable's scheduled hours, the plan's median scheduled hours, and the heuristic signals that raised a missing-dependency question (a description mentions the other, files in the same or nested directories, a sharedmetadata.owner); - for every deliverable a question names: its id, scheduled hours, float
hours,
criticalandmilestoneflags, owned file paths (sent in full, not truncated), prerequisites (id,consumes, kind) and itsdescription,artifactandownermetadata.description,artifact,ownerandconsumesare cut to 500 characters each.
Nothing else in the graph (other deliverables, other metadata keys, estimates) is sent. Do not review plans whose contents you may not share with OpenRouter.
-
Audit. The key is never logged or returned, and neither is a misplaced value of any LLM variable. Every
plan.reviewcall records oneplan.reviewaudit event, whatever its outcome (including a missing plan, a bad path or a failed review); the only unaudited case is a call rejected for its params (invalid_params). The event has exactly these fields:status:ok,review_unavailable,invalid_graph, orerror(the review failed:PLAN_NOT_FOUND,INVALID_PATH,INVALID_CAPACITIES, …);code: the error prefix forerror, else null;failure_class: for areview_unavailablecaused by a failed judge call, its class (unauthorized,rate_limited,timeout,upstream,decode,transport,invalid_request); null otherwise (including no key and an unusable LLM setting);plan_id: the stored plan reviewed, null for an inlinegraphorpath;question_count: questions sent (0 when the judge was not called);jev_called: whether the judge was called;prompt_hash: sha256 of the canonical request, null when the judge was not called;model: the model the provider reported, else the configured model when the call failed; null when the judge was not called;endpoint: the judge endpoint's host; null when no judge is configured, lint found errors, or the review ended inerror.
The prompt itself is never recorded.
Use as a library
The CPM kernel is also a plain Rust library, independent of MCP:
The crate-level docs (cargo doc --open, or docs.rs
once a current version is published there) have a runnable example that drives the
planner through submit, lease and complete.
llm::jev::JevJudge (the plan.review judge) owns its HTTP client and
connection pool. A pooled connection is driven by the tokio runtime that
opened it, so use a judge (and its clones) from one runtime, and build a new
judge for another runtime.
Praxec
cpm-planner is fully standalone: it speaks plain MCP and has no code dependency on any particular client. Praxec packs use it as an MCP tool. To wire it into a praxec workflow, declare an MCP connection (protocol only, no shared code):
The easiest way to get it, plus a workflow pack that uses it, up and running is the one-command setup:
See the pack registry for this tool's provider coordinates (container image or release binary) and which packs depend on it.
Environment variables
Set these in a client's env block (or -e/--env flag, or docker run -e). All are optional.
Leases default to 5 minutes. For long-running work pass ttl_seconds (≤ the server maximum) on acquire/heartbeat, and heartbeat at least every ttl/3.
Documentation
- docs/README.md: index of the project documentation.
- docs/AGENT-INSTALL.md: install, register and verify cpm-planner for your AI coding tool.
- docs/architecture.md: modules, request flow, store schema and concurrency.
- docs/releasing.md: the release runbook.
- CONTRIBUTING.md, SECURITY.md, SUPPORT.md, CHANGELOG.md.
Contributing
Contributions are welcome. See CONTRIBUTING.md for the toolchain, the gates and the branch model. Everyone taking part agrees to the Code of Conduct.
License
Source: README.md at commit 1fdd3af
Tools
0Version history
1- v0.2.1LatestOct 11, 2026


