@metantel/forge
v1.0.0-beta.1
Published
Metantel Forge — one command from a web application to a deployable MCP server. Fuses browser HAR captures, Gateway tool catalogs, data-atlas drafts, and OpenAPI specs into a finished, compiling TypeScript MCP project.
Maintainers
Readme
Metantel Forge
One command from a web application to a deployable MCP server. No code, no certificate, no ceremony.
npx @metantel/forge <workspace>
# → <workspace>/.Metantel-Spectre-MCPs/<name>/ (a finished TypeScript MCP project)The forge is the free capture→MCP compiler: it auto-discovers every piece of evidence a
workspace holds and fuses it into a single, compiling, runnable, deployable MCP server
built on the standardized MCP SDK. You do two things afterward: npm run verify, then
deploy the folder.
(Working from this repo instead of npm: node spectre-forge/bin/spectre-forge.mjs <workspace> —
the CLI is also installed as the legacy alias spectre-forge.)
Quickstart: wire the result into your agent
Forge, verify, then point your client at the generated project:
Claude Desktop — add to claude_desktop_config.json (the generated README has the
exact block with your paths filled in):
{ "mcpServers": { "my-app": {
"command": "node", "args": ["<outDir>/dist/index.js"],
"env": { "APP_BASE_URL": "https://your-app.example.com", "APP_USERNAME": "…", "APP_PASSWORD": "…" }
} } }Claude Code — claude mcp add my-app -- node <outDir>/dist/index.js (set the same
APP_* env vars with -e).
Cursor — Settings → MCP → Add server, command node <outDir>/dist/index.js, same env.
As a service — docker compose up --build inside the generated project → streamable
HTTP MCP at http://localhost:3901/mcp, bearer-token gated.
The four roads in (use any, combine all)
| Source | Where it comes from | What it yields |
|---|---|---|
| .trish-MRC/tool-catalog.json | Gateway capture → synthesize → accept (Flow Console) | refined, session-bound HTTP tools |
| .spectre/mcp-draft.json | the data-atlas draft (Flow Console's logic map) | read-only SQL tools + write bindings |
| *.har | any browser: DevTools → Network → Export HAR | tools from real observed traffic — zero tooling |
| openapi.json / swagger.json | an existing API spec, if the app has one | tools from the declared contract |
The HAR road is the no-code path: open the app, click through the actions you want as agent tools, export the HAR into the workspace, run the forge. Language of the target app is irrelevant — PHP, Java, Rails, .NET, Node all speak HTTP; the forge treats the app's own request boundary as the API (Metantel's "everything is an API" thesis).
What the forge understands — beyond form-POST CRUD
The classifier decomposes modern API shapes instead of collapsing them onto the URL path:
- GraphQL — one
POST /graphqlbecomes one tool per operation (keyed on the operation name), tier inferred fromqueryvsmutation; the captured query is replayed with the agent's variables swapped in. Error envelopes (HTTP 200 +errors[]) are surfaced as failures. - tRPC — batched
/trpc/a,b,cprocedures are split into per-procedure tools and replayed non-batched (GET …?input=for queries,POSTfor mutations; superjson{json:…}preserved). - JSON-RPC — keyed on
body.method; batched arrays decompose call-by-call. - Classic form-POST / REST — path templated (
/invoice/123→/invoice/{id}), body/query params typed and classified. - Multipart uploads — when the capture names the parts, the endpoint becomes a tool whose
file parts are call-time inputs (
base64:…, afile://path, or raw text). Captured file content is never replayed. Opaque multipart bodies are still excluded, with the reason stated.
Honest by construction
The forge is conservative on purpose — the failure mode it refuses to have is "looks finished, silently wrong." Concretely:
- Secrets & PII never leave the capture. Captured values that look like passwords, tokens,
API keys, or personal fields are withheld from the generated source — the field stays as a
typed input you supply at call time. The shared rule set lives in
lib/scrub-rules.json(one source for the forge and the Recorder), and every withholding is counted by class in the manifest'sscrubReport— counts, never values. - Noise, login, logout, opaque uploads, and all-failed calls are excluded — and every
exclusion is reported (CLI, README,
spectre.manifest.json), not silently dropped. - Every tool carries a confidence (high / medium / low). A single-observation classification, a suspected multi-step-workflow step, or a spec-only promise is downgraded, not dressed up.
- If nothing survives classification, the forge refuses with the reasons — an honest "this app's shape isn't a fit yet" beats a bogus tool.
Fusion, not concatenation
When two sources describe the same operation, the forge merges them: the catalog's refined
binding wins, but a HAR's extra observed parameters are folded in, and a data-atlas write that
maps to a captured route annotates that tool instead of duplicating it. Priority is
catalog > HAR > OpenAPI; SQL reads always add; unbound writes are surfaced as clearly-marked
drafts (with the exact route to capture next) rather than guessed. GraphQL/tRPC/JSON-RPC dedupe
on the operation, not just the path.
What the generated server does at runtime
- Auth. Form login (fresh-CSRF-per-request) with a one re-login retry on a 401/419 auth
challenge; or bring-your-own
APP_COOKIEfor auth the vault can't perform (OAuth/OIDC, SAML, MFA, bot-protected logins). An expired static cookie yields a clear "refresh APP_COOKIE" error. - CSRF, three ways. Hidden field,
<meta name=csrf-token>, or a header token read from a cookie (Laravel/Angular-style XSRF) — detected, never assumed, minted fresh per request. - Drift is surfaced, not hidden. A form field the capture expected that's gone from the live page, or an unexpected 4xx on a bound action, is annotated in the tool's output so you know to re-forge. Each drift signal also bumps a local counter, and at most once a day the server prints where the self-healing version lives (Metantel Gateway). The link is printed, never fetched — a forged server never phones home.
- Bot protection (Cloudflare/Akamai/PerimeterX) is detected and reported, not blindly retried.
Verify before you trust it
Every generated project ships npm run verify — the step the forge itself cannot run (it has no
credentials at forge time). It authenticates against your APP_BASE_URL, exercises every read
tool, and reports pass/fail per tool (--actions to include mutations, against a twin/UAT).
Add -- --report md to also write verify-report.md — a shareable verification dossier (tool
names, verdicts, confidence; no credentials, no captured values).
What you get (both runtime paths, always)
- OS-native — stdio.
npm start→ Claude Desktop, Claude Code, Cursor, any local MCP client. - Sandbox / container — streamable HTTP.
docker compose up→ an MCP service at/mcp, bearer-token gated, session-managed.
Every generated project includes: the TypeScript MCP server, a hardened TS port of Flow Console's
SessionVault, a driver-adaptive read-only SQL layer (postgres / mysql / sqlite), verify.ts,
.env.example, Dockerfile + docker-compose.yml, a provenance + confidence + scrub manifest,
and a README wiring guide. Dependencies are exact-pinned with a committed lockfile (plus a
zod override forcing one copy) so npm ci is deterministic months later.
Templates
templates/ holds verified capture recipes for well-known apps (what to click, what the fields
mean, how to boot a disposable twin). See templates/README.md; kanboard/ is the reference.
Telemetry — off by default, shape-level only
metantel-forge --telemetry on opts into anonymous shape-level events: road used, tool
kinds/counts, confidence histogram, refusal codes, generator version. Never URLs, names,
params, bodies, or hostnames. Refusal codes are how we rank which app shapes get support next.
--telemetry off (or simply never opting in) means nothing is ever sent.
Known limits (stated, not hidden)
- Auth the vault can't perform (interactive OAuth/OIDC, SAML, MFA) needs a browser-derived
APP_COOKIE; when it expires the server says so — it can't renew an externally-minted session. - Multipart uploads with opaque bodies (no named parts in the capture) are excluded and reported; so are parts whose content is signed/derived server-side.
- The artifact is a snapshot. The Gateway's self-heal / Proof-of-Heal / Preflight are platform features; the exported server detects drift and tells you to re-forge, but does not repair itself.
Safety
Reads are safe by construction; action tools replay mutating endpoints — point the server at a
twin/UAT first, npm run verify --actions, then repoint at prod via .env. SQL tools are
read-only and LIMIT-capped. Nothing about the target app's code runs here; the forge only reads
evidence and the generated server only calls the deployed app's own endpoints, as a browser would.
Developing the forge itself
node test/run.mjs # golden IR per road + emit snapshots + honesty checks
UPDATE_GOLDEN=1 node test/run.mjs # after a deliberate, reviewed output change
node test/compile-check.mjs # every emitted fixture must pass tsc --noEmit (needs network once)
node tools/gen-lockfiles.mjs # after bumping generated-project dependency pinsPublishing is CI-only (.github/workflows/publish-forge.yml): tag forge-v*, provenance-attested,
no postinstall, test-gated. The unscoped metantel-forge stub package (npm-stub/) guards the name.
Metantel Forge — the agentic-era MCP builder. Free forever; the self-healing version is Metantel Gateway.
