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

**The director of the forge pipeline.** An MCP server that tells Claude Code *which phase to
run next, which subagent must run it, and what it must produce*, then validates the evidence
before letting the flow advance.

**No phase is executed by the main agent.** Each one goes to a dedicated subagent with the
phase's full criterion; the main agent directs β€” it asks for the phase, launches the subagent,
and closes the phase with its evidence. forge never runs the work itself, and never blocks
either: see [the contract](./PROTOCOL.md) for who enforces what.

πŸ‡ͺπŸ‡Έ [LΓ©elo en espaΓ±ol](./README.es.md)

---

## The idea

A long piece of work β€” design a feature, build it, test it, ship it β€” is easy to do out of
order, skip a step of, or declare "done" without proof. forge turns that flow into a
**pipeline of phases** that can only advance in order, and only when each phase hands back
real evidence.

- forge **says which phase is next**, **which subagent runs it**, and exactly what it expects
  (the full criteria, not a summary) β€” as a brief ready to hand to an `Agent`.
- **A dedicated subagent executes** the phase β€” reading code, writing it, running tests. The
  main agent directs; it does not build.
- The subagent **records what it decided and why** as it goes, so the reasoning outlives the
  session instead of dying with the context.
- The phase is **reported done with evidence**; forge **validates** it and **advances**.
- You **cannot skip a phase**, cannot close one with empty or fake evidence, and cannot
  finish before every phase is closed.

Why a subagent and not the main agent: 13 phases of full criteria would flood the main
context, and each phase deserves to start clean. The main agent keeps the thread with the
user; the subagents keep the work.

The state lives in **SQLite** (`node:sqlite`, a Node built-in β€” no native build step), so a
run **resumes in any session**: Claude's context does not survive a close, a compaction or
picking up the next day β€” the flow's state does.

---

## The 13 phases

```
classify β†’ clarify β†’ setup β†’ precondition β†’ design β†’ plan β†’ build
        β†’ gates β†’ qa β†’ reconcile β†’ contraste β†’ reflect β†’ deliver
```

| Phase | Subagent | What it does | Asks the user | Optional |
|---|---|---|:-:|:-:|
| `classify` | analyst | Classify the request's nature (QUESTION / MICRO / STANDARD / HIGH-RISK) and scope. | | |
| `clarify` | analyst | Detect ambiguities that change the product, with options and consequences. | **yes** | |
| `setup` | architect | Decide the stack and its REAL versions (via `npm view` / official CLIs, never from memory); scaffold, install deps, strict linter. | | |
| `precondition` | analyst | Verify the conditions to safely start the build are actually met (tools present, env ready). | | |
| `design` | architect | Before coding: brainstorm the solution, a UX/UI design brief, a QA plan with edge cases, a code-quality guide β€” persisted as artifacts. | | |
| `plan` | architect | Decompose the work into atomic tasks with disjoint file ownership, grouped into independent blocks. | | |
| `build` | builder | Implement the plan β€” reusing what exists, never rewriting a whole file, respecting the strict linter. | | |
| `gates` | verifier | Run the repo's real gates (strict lint + build + tests). Truth is the exit code, not the model's self-report. | | |
| `qa` | qa | Verify for real: run the app end to end, then ATTACK it (odd inputs, limits, impossible states) and report what broke. | | |
| `reconcile` | builder | Only if parallel work may have duplicated logic or created conflicts β€” resolve them. | | **yes** |
| `contraste` | reviewer | An independent review that does NOT know the QA verdict, exploring the finished build with fresh eyes. | | |
| `reflect` | analyst | Look back on how this run went (what passed, what fell back, what failed) and extract lessons. | | |
| `deliver` | releaser | Publish per the request (remote push, deploy). Never reports "online" without a real URL; skips explicitly with a reason if it does not apply. | | **yes** |

Each phase carries a `goal` (short instruction), a full `systemPrompt` (the complete criteria,
handed to the subagent verbatim), an `agent` role, a `delegationBrief` (one subagent or several
in parallel β€” `build` opens by block, `contraste` demands a blind one), the `skills` to load,
and flags for whether it needs the user or is optional. All in `src/phases.ts`.

The `agent` field is a **role**, not a specific agent name, because every project has its own:
the main agent resolves the role against whatever agents exist and falls back to a
general-purpose one.

One consequence worth stating: `clarify` runs in a subagent, but **the subagent does not ask
the user**. It detects the ambiguities and returns them; the main agent brings them to the
user, because it is the one holding the conversation.

---

## Evidence is validated, not trusted

forge does not accept "done" as a string. `forge_complete_phase` validates the **evidence**
each phase must hand back, and rejects the close if it does not hold up:

- `gates` requires the real exit codes (lint / build / test) and they must all be `0`.
- `qa` requires a structured result: it passed, and it was actually attacked.
- `clarify` (a user decision) requires an explicit `userConfirmed`.
- An **optional** phase can only be skipped with a stated reason.

So a phase cannot be closed with an invented summary. The pipeline advances on proof.

---

## The skills library (128)

`src/skills.ts` + the `skills/` folder ship 128 skills, each with its own `SKILL.md`,
versioned in the repo (forge is self-contained β€” it does not depend on anything external for
these). Each phase declares which skills it loads; the domain map (`SKILL_MAP`) says which
skills belong to which domain. Skills load **on demand** β€” Claude asks for the list with
`forge_skills` and the content of a specific one with `forge_skill(name)`, never all at once.

---

## The 10 tools

| Tool | What it does |
|---|---|
| `forge_start(request, cwd)` | Start a new run; returns the first phase (`classify`) as a delegation brief. |
| `forge_status(runId?)` | Which phase a run is in and its progress (`[x]` closed, `[>]` current, `[ ]` pending). |
| `forge_next(runId?)` | The CURRENT phase as a **brief ready to hand to a subagent**: which role to use, how to split it, the skills to load, and the full `systemPrompt` to pass verbatim. |
| `forge_start_phase(runId?, agentId)` | Called right BEFORE launching the subagent. Starts the phase clock and records who runs it. |
| `forge_log(runId?, kind, title, detail?)` | Record a `decision` (with its reason), an `evidence` (real exit codes, QA verdict), or a `note` in the timeline. |
| `forge_complete_phase(runId?, summary, evidence, agentId?)` | Close the current phase with validated evidence, then advance. If it was the last phase, mark the run `done`. |
| `forge_timeline(runId?, write?)` | Render the run: Markdown with a Mermaid gantt + trackable JSON. Writes `.ai/forge/<runId>.md` and `.json` unless `write=false`. |
| `forge_tasks()` | List active runs β€” to resume from any session without re-reading context. |
| `forge_skills(phase?)` | List the full skills library, or filtered by domain if a phase is given. |
| `forge_skill(name)` | Return a specific skill's `SKILL.md` to load and apply. |

### The phase token

`forge_next` embeds a `[forge:<runId>:<phase>]` marker in the subagent prompt. It exists so the
`forge-flow` gate can check, when it sees an `Agent` call, that the prompt really is the current
phase's brief β€” without it, "I delegated this phase" would be self-report and an invented brief
would pass.

---

## The timeline

Every run keeps an append-only log of what actually happened: which subagent ran each phase and
how long it took, what was decided **and why**, and the real verification evidence. It lives in
SQLite next to the flow state, so it survives a session close or a compaction.

`forge_timeline` renders it two ways, because they are read differently:

- **Markdown with a Mermaid gantt** β€” to look at. GitHub and VS Code render it as-is, so no
  tool or server is needed to see where the time went.
- **JSON** β€” to track. Stable and diffable between runs, so you can mechanically compare
  whether the flow is getting better.

```mermaid
gantt
    title Time per phase
    dateFormat x
    axisFormat %H:%M:%S
    section Pipeline
    Build (builder) :done, 1788703521000, 1788703941000
    Deterministic gates (verifier) :done, 1788703941500, 1788704019500
    QA - it works and holds (qa) :done, 1788704020000, 1788704280000
```

Both are written to `.ai/forge/<runId>.md` and `.ai/forge/<runId>.json` inside the project, so
the record is versioned alongside the code it documents.

---

## Install

Requires **Node β‰₯ 22.5** (for `node:sqlite`). Nothing else β€” no native build step, no
database to provision.

### On any machine (published package)

Add this to Claude Code's `.mcp.json` (project) or your user config. There is nothing to
install first: `npx` fetches it on the first run.

```json
{
  "mcpServers": {
    "forge": { "command": "npx", "args": ["-y", "@devrik-tools/forge-mcp"] }
  }
}
```

Pin a version when you want the same one everywhere:
`"args": ["-y", "@devrik-tools/forge-mcp@1.1.0"]`.

Prefer it resident rather than fetched each time:

```bash
npm install -g @devrik-tools/forge-mcp
```

```json
{ "mcpServers": { "forge": { "command": "forge-mcp" } } }
```

Restart the session (or run `/mcp`) and check that `forge_tasks` answers.

### Checking what you actually have

`/mcp` lists forge with the version it announces in its handshake, which is read from the
installed `package.json` β€” so that number IS the installed version. (Before 1.1.1 it was
pinned to `0.1.0` and told you nothing; if you see `0.1.0`, you are on an older build no
matter what npm says.)

From a shell:

```bash
npm view @devrik-tools/forge-mcp version   # what the registry has
npm ls -g @devrik-tools/forge-mcp          # what this machine has, if installed globally
```

`npx` keeps its own cache, so a machine can run an older copy than the registry holds until
the cache turns over. `npx -y @devrik-tools/forge-mcp@latest` forces the current one.

**What does NOT travel with the install.** The state lives in a global SQLite file at
`~/.forge/forge-mcp.db`, which is per machine: a second PC starts with no runs, and a run
started on one machine is not visible on the other. Point `FORGE_MCP_DB` at a synced path if
you want them to share, and read the concurrency note below before you do β€” two machines
writing one SQLite file over a sync service is not the same as two processes on one disk.

The 128 skills ship inside the package, so a fresh machine needs no extra fetch for them β€”
at the price of size: 4.1 MB packed, 12.9 MB on disk, 901 files, 874 of them skills. `npx`
pays that once and caches it. That trade is the point of the design, not an oversight: forge
carries its own arsenal instead of depending on whatever happens to be installed.

### From a clone (to work on forge itself)

```bash
git clone https://github.com/DevRik99/forge-mcp
cd forge-mcp
npm install
npm run build
```

```json
{ "mcpServers": { "forge": { "command": "node", "args": ["dist/server.js"] } } }
```

---

## How a run resumes

The state lives in a single **global** SQLite DB at `~/.forge/forge-mcp.db` (override with
`FORGE_MCP_DB`), so every project shares one store and runs are told apart by their `cwd`.
Two tables:

- `runs`: one row per run (`id`, `request`, `cwd`, `current_phase`, `status`, timestamps).
- `phase_artifacts`: one row per closed phase (`run_id`, `phase`, `summary`, `closed_at`,
  `started_at`, `agent_id`) β€” the real decision/artifact reported, not a boolean flag, plus
  who ran it and how long it took.
- `timeline`: append-only, one row per event (`phase_started`, `phase_closed`, `decision`,
  `evidence`, `note`) with its agent and timestamp.

After a lost session (close, compaction, next day), any new Claude session with this MCP
connected can:

1. Call `forge_tasks()` to see which runs are still active and in what phase.
2. Call `forge_next(runId)` to get the current phase's full `systemPrompt` again β€” Claude
   does not need to remember anything; forge hands it back verbatim.
3. Read the closed phases' artifacts via `forge_status` so nothing already decided (e.g. in
   `clarify`) is re-asked.

---

## Guarantees (and honest limits)

forge **enforces**: the phase order, closing every phase before finishing, and validated
evidence per phase (no fake `gates`/`qa`, no skipping user decisions or optional phases
without a reason). Under concurrency, closing a phase is atomic β€” a stale double-close is
rejected, not silently applied.

forge **cannot** stop you from editing the project *without* using it at all, and it cannot
stop the main agent from doing a phase itself β€” an MCP only sees its own tools, not your
`Edit`/`Write`/`Bash`. Trying to enforce from here would be a paper lock.

That half is the `forge-flow` gate's job in
[claude-gates](https://github.com/DevRik99/claude-gates), which *does* see those calls and can
tell whether they come from the main agent or from a subagent. The two pair like this:

| | forge-mcp | claude-gates |
|---|---|---|
| Sees | only its own tools | every `Edit`/`Write`/`Bash`/`Agent`, and **who** made it |
| Guarantees | the criterion is complete; no phase closes on fake evidence | the main agent does not touch the code; only a subagent does |
| Role | director β€” **never blocks** | lock β€” **knows nothing about phases** |

They are coupled by exactly one thing: the SQLite DB. The gate reads it, forge writes it,
neither imports the other's code. Full contract in [PROTOCOL.md](./PROTOCOL.md).

## License

MIT.

TDQS

A4.1/5.0

Scored across 7 tools

Disambiguation5/5

Each tool has a distinct job: start a run, advance a phase, check progress, list runs, and load skill metadata/content. The only close pair, forge_next and forge_status, is separated by guidance vs. state, so an agent should not misselect.

Naming Consistency4/5

All tools share the forge_ prefix and snake_case, making them predictable. However, the pattern mixes verbs (start, complete_phase) with bare nouns (status, tasks, skills, skill), so it is not a fully consistent verb_noun convention.

Tool Count5/5

Seven tools is well-scoped for a pipeline-orchestration server. Each tool covers a necessary part of the run lifecycle or skill access without redundancy or bloat.

Completeness4/5

The core lifecycle is covered: start, advance, inspect status, list runs, and load skills. The only notable gap is the lack of an explicit cancel/abort operation for a run, though that may be intentionally unsupported by the guided pipeline model.

Maintenance

ActivityMaintained
ResponsivenessNo issues