@devrik-tools/forge-mcp
v1.1.1
Published
MCP director for the forge pipeline: tells Claude which phase to run, validates each phase with real evidence, and persists state in a global SQLite DB to resume across sessions. Claude is the executor.
Maintainers
Readme
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 for who enforces what.
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:
gatesrequires the real exit codes (lint / build / test) and they must all be0.qarequires a structured result: it passed, and it was actually attacked.clarify(a user decision) requires an explicituserConfirmed.- 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.
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, 1788704280000Both 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.
{
"mcpServers": {
"forge": { "command": "npx", "args": ["-y", "@devrik-tools/forge-mcp"] }
}
}Pin a version when you want the same one everywhere:
"args": ["-y", "@devrik-tools/[email protected]"].
Prefer it resident rather than fetched each time:
npm install -g @devrik-tools/forge-mcp{ "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:
npm view @devrik-tools/forge-mcp version # what the registry has
npm ls -g @devrik-tools/forge-mcp # what this machine has, if installed globallynpx 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)
git clone https://github.com/DevRik99/forge-mcp
cd forge-mcp
npm install
npm run build{ "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:
- Call
forge_tasks()to see which runs are still active and in what phase. - Call
forge_next(runId)to get the current phase's fullsystemPromptagain — Claude does not need to remember anything; forge hands it back verbatim. - Read the closed phases' artifacts via
forge_statusso nothing already decided (e.g. inclarify) 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, 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.
License
MIT.
