create-restforge-skills 1.0.0 → 1.0.2
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/cli/index.js +250 -250
- package/cli/mcp.js +103 -94
- package/package.json +2 -1
- package/skills/restforge/SKILL.md +230 -773
- package/skills/restforge/agents/openai.yaml +4 -4
- package/skills/restforge/references/auth.md +185 -145
- package/skills/restforge/references/backend-pipeline.md +388 -0
- package/skills/restforge/references/data-seeding.md +27 -0
- package/skills/restforge/references/dbschema-catalog.md +2 -2
- package/skills/restforge/references/field-validation.md +6 -4
- package/skills/restforge/references/frontend-pipeline.md +178 -0
- package/skills/restforge/references/rdf-advanced.md +1 -1
- package/skills/restforge/references/troubleshooting.md +144 -0
package/cli/mcp.js
CHANGED
|
@@ -1,94 +1,103 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* MCP registration for create-restforge-skills.
|
|
5
|
-
*
|
|
6
|
-
* The skill is inert without the RESTForge MCP server, so registration is the
|
|
7
|
-
* default behavior. This module merges (never overwrites) a `restforge` entry
|
|
8
|
-
* into a client's MCP config and backs the file up first.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const fs = require('fs');
|
|
12
|
-
const path = require('path');
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
function
|
|
27
|
-
if (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MCP registration for create-restforge-skills.
|
|
5
|
+
*
|
|
6
|
+
* The skill is inert without the RESTForge MCP server, so registration is the
|
|
7
|
+
* default behavior. This module merges (never overwrites) a `restforge` entry
|
|
8
|
+
* into a client's MCP config and backs the file up first.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
// A cold `npx -y @restforgejs/mcp-server` (empty npm cache) took 13.3 s to answer
|
|
15
|
+
// `initialize` on 2026-10-02 (issue #002), above the 10 s default startup window in
|
|
16
|
+
// the Codex documentation. Codex reads `startup_timeout_sec` per MCP server, so its npx
|
|
17
|
+
// entry gets a wider window. Claude Code and Cursor have no such key in their JSON config.
|
|
18
|
+
const CODEX_NPX_STARTUP_TIMEOUT_SEC = 60;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Build the MCP server entry.
|
|
22
|
+
* - 'npx' (default): no separate global install — npx fetches on first run.
|
|
23
|
+
* - 'global': uses the globally-installed `restforge-mcp` binary (faster start).
|
|
24
|
+
* - client 'codex' + 'npx': adds `startup_timeout_sec` for the cold npx start.
|
|
25
|
+
*/
|
|
26
|
+
function buildEntry(mcpCommand, client) {
|
|
27
|
+
if (mcpCommand === 'global') {
|
|
28
|
+
return { command: 'restforge-mcp' };
|
|
29
|
+
}
|
|
30
|
+
const entry = { command: 'npx', args: ['-y', '@restforgejs/mcp-server'] };
|
|
31
|
+
if (client === 'codex') entry.startup_timeout_sec = CODEX_NPX_STARTUP_TIMEOUT_SEC;
|
|
32
|
+
return entry;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function readConfig(file) {
|
|
36
|
+
if (!fs.existsSync(file)) return { existed: false, data: {} };
|
|
37
|
+
const raw = fs.readFileSync(file, 'utf8').trim();
|
|
38
|
+
if (!raw) return { existed: true, data: {} };
|
|
39
|
+
let data;
|
|
40
|
+
try {
|
|
41
|
+
data = JSON.parse(raw);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
const e = new Error(`config is not valid JSON: ${err.message}`);
|
|
44
|
+
e.code = 'EBADJSON';
|
|
45
|
+
throw e;
|
|
46
|
+
}
|
|
47
|
+
if (data === null || typeof data !== 'object' || Array.isArray(data)) {
|
|
48
|
+
const e = new Error('config root is not a JSON object');
|
|
49
|
+
e.code = 'EBADJSON';
|
|
50
|
+
throw e;
|
|
51
|
+
}
|
|
52
|
+
return { existed: true, data };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Merge the `restforge` MCP server into `file`.
|
|
57
|
+
* Returns { status, backup?, error? } where status is one of:
|
|
58
|
+
* registered | updated | already-registered | would-register | would-update | error
|
|
59
|
+
* Never overwrites unrelated keys; backs up an existing file before writing.
|
|
60
|
+
*/
|
|
61
|
+
function registerMcp(file, entry, opts) {
|
|
62
|
+
let cfg;
|
|
63
|
+
try {
|
|
64
|
+
cfg = readConfig(file);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
if (err.code === 'EBADJSON') {
|
|
67
|
+
// Refuse to touch a config we cannot safely parse.
|
|
68
|
+
return { status: 'error', error: err.message };
|
|
69
|
+
}
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const data = cfg.data;
|
|
74
|
+
const servers =
|
|
75
|
+
data.mcpServers && typeof data.mcpServers === 'object' && !Array.isArray(data.mcpServers)
|
|
76
|
+
? data.mcpServers
|
|
77
|
+
: {};
|
|
78
|
+
|
|
79
|
+
const existing = servers.restforge;
|
|
80
|
+
if (existing && JSON.stringify(existing) === JSON.stringify(entry)) {
|
|
81
|
+
return { status: 'already-registered' };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const willUpdate = Boolean(existing);
|
|
85
|
+
if (opts.dryRun) {
|
|
86
|
+
return { status: willUpdate ? 'would-update' : 'would-register' };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let backup;
|
|
90
|
+
if (cfg.existed) {
|
|
91
|
+
backup = file + '.bak';
|
|
92
|
+
fs.copyFileSync(file, backup);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
servers.restforge = entry;
|
|
96
|
+
data.mcpServers = servers;
|
|
97
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
98
|
+
fs.writeFileSync(file, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
99
|
+
|
|
100
|
+
return { status: willUpdate ? 'updated' : 'registered', backup };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = { buildEntry, registerMcp };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-restforge-skills",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Install the RESTForge Agent Skill and MCP server configuration into Claude Code, Cursor, and OpenAI Codex.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"test": "node --test",
|
|
11
|
+
"prepublishOnly": "node --test test/package-contents.test.js && node scripts/audit-references.js",
|
|
11
12
|
"audit-references": "node scripts/audit-references.js"
|
|
12
13
|
},
|
|
13
14
|
"files": [
|