Skip to main content
Glama
README.md
# forecast-mcp

**Give your AI a Monte Carlo forecasting engine.** A [Model Context Protocol](https://modelcontextprotocol.io) server that turns three-point estimates and historical throughput into calibrated, probabilistic forecasts: project timelines, cost ranges, and "when will it be done?" delivery dates.

Fully local. No API keys. No network calls.

---

## Why this exists

Large language models are genuinely bad at probability. Ask one for a project deadline and it will confidently invent a single number, or "simulate" a distribution it cannot actually compute. Monte Carlo forecasting is exactly the kind of work an LLM should **delegate to a tool**: run thousands of seeded random trials and report the real distribution.

`forecast-mcp` gives any MCP-capable client (Claude Desktop, Claude Code, Cursor, and others) five tools to do that properly, so instead of *"this will take about three weeks"* you get:

> P50 = 19.5 days, P80 = 24.1 days, P95 = 28.7 days, and an 84% chance of finishing within your 25-day target.

## Install

Run it straight from npm with `npx` (no install needed):

```bash
npx forecast-mcp
```

Then register it with your client. For **Claude Desktop** / **Claude Code**, add to your MCP config:

```json
{
  "mcpServers": {
    "forecast": {
      "command": "npx",
      "args": ["-y", "forecast-mcp"]
    }
  }
}
```

That's it. The server speaks stdio and needs no configuration, credentials, or internet access.

> **Running from source** (before the npm package is published, or to hack on it): clone this repo, run `npm install && npm run build`, then point your client at it with `"command": "node", "args": ["/absolute/path/to/forecast-mcp/dist/index.js"]`.

## Tools

| Tool | What it answers |
| --- | --- |
| `forecast_duration` | How long will the whole project take? (Monte Carlo over per-task 3-point estimates) |
| `forecast_cost` | What will it cost, with contingency? (Monte Carlo over cost line items) |
| `forecast_completion` | When will it be done, from our actual throughput? (flow-based, "no estimates") |
| `pert_estimate` | Quick analytical 3-point estimate + confidence levels (instant, no simulation) |
| `sensitivity_analysis` | Which tasks drive the uncertainty? (tornado-chart data) |

Every tool takes optimistic / most-likely / pessimistic inputs (except `forecast_completion`, which uses throughput history) and returns both a human-readable summary and structured JSON.

### Distributions

`forecast_duration`, `forecast_cost`, and `sensitivity_analysis` accept a `distribution`:

- `pert` (default) - beta-PERT, the project-management standard; weights the most-likely value.
- `triangular` - simple, bounded.
- `normal` - symmetric bell curve (std dev derived from the range).
- `lognormal` - right-skewed; models "usually fine, occasionally very late".
- `uniform` - flat between optimistic and pessimistic.

You can also override the distribution per item.

### Reproducibility

Every tool accepts an optional `seed` and **defaults to a fixed seed**, so the same inputs always produce the same forecast. Change the seed to explore alternate random streams.

## Deploying remotely (Streamable HTTP)

`npx forecast-mcp` is stdio-only, which is fine for a local client but can't be reached over a network. The same binary also speaks [Streamable HTTP](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http), so it can run as a standalone deployable service (Docker, Render, Fly, Railway, Cloud Run, a VM, etc.) that any remote MCP client can call.

**One entry point, two transports** - `node dist/index.js` picks the transport at runtime:

- No `PORT` and no `MCP_TRANSPORT` set -> **stdio** (the default, used by `npx forecast-mcp`).
- `PORT` set (most PaaS platforms set this automatically), or `MCP_TRANSPORT=http` -> **Streamable HTTP**, served at `POST /mcp`, with a `GET /healthz` health check.

The HTTP server is **stateless**: every request builds a fresh server instance (`sessionIdGenerator: undefined`, the pattern the MCP SDK recommends for this case). That's safe here because every forecast-mcp tool is a pure function - there is no session state to preserve between requests, so nothing is lost by not keeping one.

### Run it locally over HTTP

```bash
npm run build
npm run start:http   # MCP_TRANSPORT=http PORT=3000 node dist/index.js
curl http://localhost:3000/healthz
```

### Docker

```bash
docker build -t forecast-mcp .
docker run -p 3000:3000 forecast-mcp
```

The image always runs in HTTP mode (that is the point of containerizing it); most platforms inject their own `PORT` at deploy time, which overrides the image's default of 3000.

### Environment variables (HTTP mode only)

| Variable | Default | Purpose |
| --- | --- | --- |
| `PORT` | `3000` | Port to listen on. Setting this alone is enough to switch to HTTP mode. |
| `MCP_TRANSPORT` | (unset) | Set to `http` to force HTTP mode even without `PORT`. |
| `HOST` | `0.0.0.0` | Interface to bind. |
| `MCP_ALLOWED_HOSTS` | (unset) | Comma-separated list of allowed `Host` header values, for DNS-rebinding protection when binding to a non-localhost address. |

### Security note

These tools are read-only pure computations with no filesystem, network, or credential access, so the blast radius of an unauthenticated call is low. Still, the server itself has no built-in authentication - if you expose it beyond a trusted network, put it behind a reverse proxy or gateway that handles auth, and set `MCP_ALLOWED_HOSTS` to suppress the DNS-rebinding warning.

## Examples

**`forecast_duration`**

```json
{
  "tasks": [
    { "name": "design", "optimistic": 2, "mostLikely": 4, "pessimistic": 8 },
    { "name": "build",  "optimistic": 5, "mostLikely": 10, "pessimistic": 30 },
    { "name": "test",   "optimistic": 1, "mostLikely": 3, "pessimistic": 6 }
  ],
  "unit": "days",
  "target": 25
}
```

returns P50/P80/P90/P95, mean, standard deviation, a distribution sparkline, and `probabilityWithinTarget` (~0.84).

**`forecast_completion`** ("when will it be done?")

```json
{
  "throughputSamples": [4, 5, 6, 5, 4, 6],
  "backlog": 40,
  "periodLabel": "sprint",
  "startDate": "2026-07-13",
  "periodDays": 14
}
```

bootstraps future sprints from your last six and returns how many sprints (and which calendar dates) it will likely take to clear 40 items. Add `splitRate` to model scope creep.

**`sensitivity_analysis`** ranks the tasks by how much each one contributes to the total variance, so you know where tightening an estimate actually moves the needle.

## Development

```bash
npm install
npm run build      # compile TypeScript to dist/
npm test           # build + run the vitest suite
npm run inspect    # open the MCP Inspector against the built server
```

The forecasting logic lives in `src/engine/` (pure, dependency-free functions) and is exercised directly by the test suite; the tools in `src/tools/` are thin MCP wrappers.

## How it works

- Seeded PRNG (`mulberry32`) for reproducible trials.
- Beta-PERT sampling via two Gamma draws (Marsaglia-Tsang); triangular, normal (Box-Muller), lognormal, and uniform samplers included.
- Per-iteration line items are summed into a project total; percentiles are computed by linear interpolation over the sorted sample.
- `sensitivity_analysis` correlates each item's draws with the total (Pearson) and reports each item's share of variance.
- `pert_estimate` is a closed-form cross-check (its mean matches the simulation's mean to within a fraction of a unit).

## License

MIT - see [LICENSE](./LICENSE).