@dforge-core/dforge-mcp 0.1.11 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +287 -0
- package/README.md +106 -72
- package/dist/server.js +2322 -388
- package/docs/creating-modules.md +21 -10
- package/package.json +46 -43
- package/resources/docs/dsl-reference.md +26 -0
- package/scripts/install-skills.mjs +165 -0
- package/skills/dforge-mcp-author/SKILL.md +147 -468
- package/skills/dforge-mcp-author/references/column-domains.md +94 -0
- package/skills/dforge-mcp-author/references/document-extraction.md +113 -0
- package/skills/dforge-mcp-author/references/field-types.md +2 -0
- package/skills/dforge-mcp-author/references/formulas.md +83 -20
- package/skills/dforge-mcp-author/references/translations.md +43 -0
- package/skills/dforge-module-build/SKILL.md +309 -0
- package/skills/dforge-module-design/SKILL.md +125 -0
- package/skills/dforge-module-ship/SKILL.md +163 -0
package/docs/creating-modules.md
CHANGED
|
@@ -42,20 +42,28 @@ Sidebar also gives you right-click commands: **Pack Module**, **Install Module t
|
|
|
42
42
|
|
|
43
43
|
## Path 3 — AI co-pilot, wizard-driven
|
|
44
44
|
|
|
45
|
-
Claude Code (or Cursor / Zed) with `dforge-mcp` connected. You describe what you want; the AI walks you through
|
|
45
|
+
Claude Code (or Cursor / Zed) with `dforge-mcp` connected. You describe what you want; the AI walks you through seven phases (0-6) via MCP tool calls, pausing at each gate for your approval. Guidance ships as four skills — a router plus one per lifecycle stage — so only the active stage is loaded.
|
|
46
46
|
|
|
47
47
|
Setup once:
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
50
|
claude mcp add dforge --scope user -- npx -y @dforge-core/dforge-mcp
|
|
51
|
-
|
|
52
|
-
#
|
|
53
|
-
|
|
54
|
-
mkdir -p ~/.claude/skills/dforge-mcp-author
|
|
55
|
-
curl -fsSL "https://cdn.jsdelivr.net/npm/@dforge-core/dforge-mcp@${VERSION}/skills/dforge-mcp-author/SKILL.md" \
|
|
56
|
-
-o ~/.claude/skills/dforge-mcp-author/SKILL.md
|
|
51
|
+
|
|
52
|
+
# Install the four authoring skills into ~/.claude/skills/
|
|
53
|
+
npx -y -p @dforge-core/dforge-mcp dforge-install-skills
|
|
57
54
|
```
|
|
58
55
|
|
|
56
|
+
That works as-is on Windows (cmd.exe / PowerShell), macOS and Linux — it's a
|
|
57
|
+
Node script, not bash. From a clone, `npm run install-skills` installs the local
|
|
58
|
+
copy instead. See the README's "Windows notes" for the WSL caveat.
|
|
59
|
+
|
|
60
|
+
| Skill | Stage |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `dforge-mcp-author` | Router — finds the current phase and hands off |
|
|
63
|
+
| `dforge-module-design` | Phase 0 — identity, requirements, design, validation |
|
|
64
|
+
| `dforge-module-build` | Phases 1-5 — entities, behavior, views, security |
|
|
65
|
+
| `dforge-module-ship` | Phase 6 — validate, pack, install |
|
|
66
|
+
|
|
59
67
|
Restart Claude Code, approve the MCP server on first prompt, then in a new conversation:
|
|
60
68
|
|
|
61
69
|
```
|
|
@@ -66,7 +74,7 @@ The wizard runs:
|
|
|
66
74
|
|
|
67
75
|
### Phase 0 — Identity, requirements, design, validation (required)
|
|
68
76
|
|
|
69
|
-
A documented chain the `dforge-
|
|
77
|
+
A documented chain the `dforge-module-design` skill enforces — the AI authors each artifact directly (drafts it, you approve, your client writes the file):
|
|
70
78
|
|
|
71
79
|
- **0a Identity** — write `CLAUDE.md` (identity + MCP-first rules + a live status tracker).
|
|
72
80
|
- **0b Requirements** — intake questions (purpose / users / dependencies / language scope), then write `docs/REQUIREMENTS.md`. **You review it before moving on.**
|
|
@@ -148,6 +156,9 @@ VS Code with the [dForge extension](https://github.com/dforge-core/dforge-editor
|
|
|
148
156
|
|
|
149
157
|
## Going further
|
|
150
158
|
|
|
151
|
-
- [
|
|
152
|
-
- [
|
|
159
|
+
- [dforge-mcp-author](../skills/dforge-mcp-author/SKILL.md) — the router: session start, tool inventory, hard rules, tool-failure protocol, resource index
|
|
160
|
+
- [dforge-module-design](../skills/dforge-module-design/SKILL.md) — Phase 0, including the intake guardrails and gap scans
|
|
161
|
+
- [dforge-module-build](../skills/dforge-module-build/SKILL.md) — Phases 1-5, the loading-policy table, core-rules cheat sheet, and the deterministic backtrack protocol
|
|
162
|
+
- [dforge-module-ship](../skills/dforge-module-ship/SKILL.md) — Phase 6 and the install-fix loop
|
|
163
|
+
- [README.md](../README.md) — full tool reference (34 tools) + maintainer docs
|
|
153
164
|
- [iash44/dForge-core](https://github.com/iash44/dForge-core) — the platform itself: source of truth for the schemas + DSL conventions
|
package/package.json
CHANGED
|
@@ -1,45 +1,48 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
2
|
+
"name": "@dforge-core/dforge-mcp",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "MCP server for dForge module authoring. Exposes 34 scaffold/patch/validate/pack/install tools and schema resources so AI agents (Claude Code, Cursor, Zed) can create and ship dForge modules.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/iash44/dForge-core",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/dforge-core/dforge-mcp.git"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"dforge-mcp": "dist/server.js",
|
|
13
|
+
"dforge-install-skills": "scripts/install-skills.mjs"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist/",
|
|
17
|
+
"docs/",
|
|
18
|
+
"resources/",
|
|
19
|
+
"skills/",
|
|
20
|
+
"scripts/install-skills.mjs",
|
|
21
|
+
"README.md",
|
|
22
|
+
"CHANGELOG.md"
|
|
23
|
+
],
|
|
24
|
+
"main": "dist/server.js",
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsup",
|
|
30
|
+
"sync-schemas": "node scripts/vendor-schemas.cjs",
|
|
31
|
+
"prepublishOnly": "node scripts/vendor-schemas.cjs && tsup",
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"install-skills": "node scripts/install-skills.mjs"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@dforge-core/dforge-cli": "^0.2.7",
|
|
38
|
+
"@dforge-core/metadata": "^0.0.10",
|
|
39
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
40
|
+
"zod": "^4.4.3"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^22.20.1",
|
|
44
|
+
"tsup": "^8.5.1",
|
|
45
|
+
"typescript": "^6.0.3",
|
|
46
|
+
"vitest": "^3.2.7"
|
|
47
|
+
}
|
|
45
48
|
}
|
|
@@ -417,6 +417,32 @@ var result = callService('studio.validate', { module_cd: [module_cd] })
|
|
|
417
417
|
if (!result.isValid) { error('Validation failed: ' + result.errors[0]) }
|
|
418
418
|
```
|
|
419
419
|
|
|
420
|
+
### Document extraction (OCR)
|
|
421
|
+
|
|
422
|
+
Full guide + intake patterns: `dforge://reference/document-extraction`.
|
|
423
|
+
|
|
424
|
+
#### `ocrExtract(fileField, endpointBaseUrl, schemaOrHints?, opts?) → string | object`
|
|
425
|
+
Send a file to the OCR service and get structured fields back. Three forms:
|
|
426
|
+
|
|
427
|
+
- **v1 (raw):** `ocrExtract([doc_file], getSetting('ocr_endpoint_url'))` → JSON **string** (you `JSON.parse`). Optional third arg = hints (array | string | object).
|
|
428
|
+
- **v2 inline schema:** `ocrExtract([doc_file], url, schema, { mode: 'extract' })` → **parsed** `{ fields, confidence?, unmatched, raw_text, meta }`.
|
|
429
|
+
- **v2 profile:** `ocrExtract([doc_file], url, null, { profile: 'module_cd.profile_cd' })` → same parsed shape, schema from `logic/extraction_profiles.json`.
|
|
430
|
+
|
|
431
|
+
v2 is selected **only** by `opts.mode` (`'extract' | 'summary' | 'raw'`) or `opts.profile` — a bare schema object without `opts.mode` stays on the v1 (raw string) contract. `opts.profile` implies `mode: 'extract'` and is mutually exclusive with an inline `schema`.
|
|
432
|
+
|
|
433
|
+
```dsl
|
|
434
|
+
var r = ocrExtract([doc_file], getSetting('ocr_endpoint_url'), null, { profile: 'proc.bill_of_lading' })
|
|
435
|
+
insert('bill_of_lading', { bl_no: r.fields.bl_no, issue_date: r.fields.issue_date })
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
#### `detectDocument(rawText) → object | null`
|
|
439
|
+
Score a document's text against every reachable extraction profile's `detect` rules and return the best match at/above its threshold: `{ profile, docType, score }` (profile module-qualified, ready for `ocrExtract(..., { profile })`) — or `null`. Advisory: malformed rules are skipped, let the human confirm.
|
|
440
|
+
|
|
441
|
+
```dsl
|
|
442
|
+
var det = detectDocument(data.raw_text) // { profile: 'proc.bill_of_lading', docType: 'bill_of_lading', score } | null
|
|
443
|
+
if (det != null) { var r = ocrExtract([doc_file], getSetting('ocr_endpoint_url'), null, { profile: det.profile }) }
|
|
444
|
+
```
|
|
445
|
+
|
|
420
446
|
### Utility
|
|
421
447
|
|
|
422
448
|
#### `entityLink(entityCd, record, description?) → link value`
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Install the dForge authoring skills into the Claude skills directory.
|
|
3
|
+
//
|
|
4
|
+
// The skills ship inside the npm tarball, but Claude Code only looks in
|
|
5
|
+
// <config>/skills — not node_modules — so they have to be copied out. There are
|
|
6
|
+
// four (a router plus three stage skills), and the router's directory also
|
|
7
|
+
// carries the shared references/ + examples/ the MCP server serves as dforge://
|
|
8
|
+
// resources, so hand-copying is error-prone.
|
|
9
|
+
//
|
|
10
|
+
// Written in Node rather than bash so it behaves identically on Windows
|
|
11
|
+
// (cmd.exe, PowerShell), macOS, Linux, Git Bash and WSL. The bash version this
|
|
12
|
+
// replaced could not run in cmd/PowerShell at all, and under WSL it resolved
|
|
13
|
+
// $HOME to the LINUX home — silently installing to /home/you/.claude while
|
|
14
|
+
// Claude Code on Windows read C:\Users\you\.claude.
|
|
15
|
+
//
|
|
16
|
+
// Usage:
|
|
17
|
+
// node scripts/install-skills.mjs # from a local checkout
|
|
18
|
+
// node scripts/install-skills.mjs --from-npm # download the published version
|
|
19
|
+
// DEST=/some/where node scripts/install-skills.mjs
|
|
20
|
+
//
|
|
21
|
+
// npm run install-skills
|
|
22
|
+
// npm run install-skills -- --from-npm
|
|
23
|
+
|
|
24
|
+
import { spawnSync } from "node:child_process";
|
|
25
|
+
import * as fs from "node:fs";
|
|
26
|
+
import * as os from "node:os";
|
|
27
|
+
import * as path from "node:path";
|
|
28
|
+
import { fileURLToPath } from "node:url";
|
|
29
|
+
|
|
30
|
+
const SKILLS = [
|
|
31
|
+
["dforge-mcp-author", "router — start here; also holds the shared\n references/ and examples/ the MCP server serves"],
|
|
32
|
+
["dforge-module-design", "Phase 0 — identity, intake, design, validation"],
|
|
33
|
+
["dforge-module-build", "Phases 1-5 — entities, behavior, views, security"],
|
|
34
|
+
["dforge-module-ship", "Phase 6 — validate, pack, install-fix loop"],
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const PKG = "@dforge-core/dforge-mcp";
|
|
38
|
+
const fromNpm = process.argv.includes("--from-npm");
|
|
39
|
+
|
|
40
|
+
// Piping into `head`/`less` closes our stdout mid-write; without this Node
|
|
41
|
+
// turns that into an unhandled EPIPE and dumps a stack trace over what is
|
|
42
|
+
// otherwise a successful install.
|
|
43
|
+
for (const stream of [process.stdout, process.stderr]) {
|
|
44
|
+
stream.on("error", (e) => {
|
|
45
|
+
if (e.code === "EPIPE") process.exit(0);
|
|
46
|
+
throw e;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Where Claude Code looks for skills. `CLAUDE_CONFIG_DIR` wins when set;
|
|
52
|
+
* otherwise `<home>/.claude/skills`. os.homedir() is the point of this being a
|
|
53
|
+
* Node script — on Windows it's %USERPROFILE% regardless of which shell (or
|
|
54
|
+
* shell emulator) invoked us.
|
|
55
|
+
*/
|
|
56
|
+
function defaultDest() {
|
|
57
|
+
if (process.env.DEST) return process.env.DEST;
|
|
58
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), ".claude");
|
|
59
|
+
return path.join(configDir, "skills");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** npm is a .cmd shim on Windows, which spawn can only launch through a shell. */
|
|
63
|
+
const NPM = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
64
|
+
|
|
65
|
+
function npm(args, opts = {}) {
|
|
66
|
+
const r = spawnSync(NPM, args, {
|
|
67
|
+
encoding: "utf8",
|
|
68
|
+
shell: process.platform === "win32",
|
|
69
|
+
...opts,
|
|
70
|
+
});
|
|
71
|
+
if (r.error) {
|
|
72
|
+
throw new Error(`Failed to run \`npm ${args.join(" ")}\`: ${r.error.message}. Is npm on your PATH?`);
|
|
73
|
+
}
|
|
74
|
+
if (r.status !== 0) {
|
|
75
|
+
throw new Error(`\`npm ${args.join(" ")}\` failed (exit ${r.status}):\n${r.stderr || r.stdout}`);
|
|
76
|
+
}
|
|
77
|
+
return (r.stdout || "").trim();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Download + unpack the published tarball into a temp dir, returning its
|
|
82
|
+
* skills/ path. Uses `npm pack` (which fetches only this package, not its
|
|
83
|
+
* ~35 MB native CLI dependency) plus `tar`, which ships with Windows 10 1803+,
|
|
84
|
+
* macOS and every Linux.
|
|
85
|
+
*/
|
|
86
|
+
function fetchFromNpm(tmp) {
|
|
87
|
+
// Resolve the real latest version rather than relying on a CDN alias that
|
|
88
|
+
// caches for hours after a publish and would serve stale skills.
|
|
89
|
+
const version = npm(["view", PKG, "version"]);
|
|
90
|
+
process.stdout.write(`Fetching ${PKG}@${version} …\n`);
|
|
91
|
+
npm(["pack", `${PKG}@${version}`, "--pack-destination", tmp], { cwd: tmp });
|
|
92
|
+
|
|
93
|
+
const tarball = fs.readdirSync(tmp).find((f) => f.endsWith(".tgz"));
|
|
94
|
+
if (!tarball) throw new Error(`npm pack produced no .tgz in ${tmp}`);
|
|
95
|
+
|
|
96
|
+
const r = spawnSync("tar", ["-xzf", path.join(tmp, tarball), "-C", tmp], { encoding: "utf8" });
|
|
97
|
+
if (r.error || r.status !== 0) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Could not extract ${tarball}: ${r.error?.message ?? r.stderr}\n` +
|
|
100
|
+
"`tar` is required (built into Windows 10 1803+, macOS and Linux). " +
|
|
101
|
+
"Alternatively clone the repo and run this script without --from-npm.",
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return path.join(tmp, "package", "skills");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function main() {
|
|
108
|
+
const dest = defaultDest();
|
|
109
|
+
let tmp;
|
|
110
|
+
let src;
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
if (fromNpm) {
|
|
114
|
+
tmp = fs.mkdtempSync(path.join(os.tmpdir(), "dforge-skills-"));
|
|
115
|
+
src = fetchFromNpm(tmp);
|
|
116
|
+
} else {
|
|
117
|
+
// Relative to this file, so it works from any cwd.
|
|
118
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
119
|
+
src = path.join(here, "..", "skills");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!fs.existsSync(src)) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`No skills directory at ${src}.` +
|
|
125
|
+
(fromNpm ? "" : " Run with --from-npm to install from the published package instead."),
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
130
|
+
let installed = 0;
|
|
131
|
+
for (const [skill] of SKILLS) {
|
|
132
|
+
const from = path.join(src, skill);
|
|
133
|
+
if (!fs.existsSync(from)) {
|
|
134
|
+
process.stderr.write(` ! skipping ${skill} (not found in ${src})\n`);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
// Replace wholesale: a stale reference file left behind is worse than a
|
|
138
|
+
// missing one, because the agent will happily author against it.
|
|
139
|
+
fs.rmSync(path.join(dest, skill), { recursive: true, force: true });
|
|
140
|
+
fs.cpSync(from, path.join(dest, skill), { recursive: true });
|
|
141
|
+
process.stdout.write(` \u2713 ${skill}\n`);
|
|
142
|
+
installed++;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (installed === 0) throw new Error(`No skills were installed from ${src}.`);
|
|
146
|
+
|
|
147
|
+
process.stdout.write(
|
|
148
|
+
`\nInstalled ${installed} skill${installed === 1 ? "" : "s"} to ${dest}\n\n`,
|
|
149
|
+
);
|
|
150
|
+
for (const [skill, blurb] of SKILLS) {
|
|
151
|
+
process.stdout.write(` ${skill.padEnd(21)} ${blurb}\n`);
|
|
152
|
+
}
|
|
153
|
+
process.stdout.write(
|
|
154
|
+
"\nRe-run after every dforge-mcp upgrade. The skill version is not checked at\n" +
|
|
155
|
+
"runtime, so stale skills against new tools will misroute calls.\n",
|
|
156
|
+
);
|
|
157
|
+
} catch (e) {
|
|
158
|
+
process.stderr.write(`error: ${e.message}\n`);
|
|
159
|
+
process.exitCode = 1;
|
|
160
|
+
} finally {
|
|
161
|
+
if (tmp) fs.rmSync(tmp, { recursive: true, force: true });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
main();
|