@lifeaitools/rdc-skills 0.24.21 → 0.24.23
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rdc",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.23",
|
|
4
4
|
"description": "RDC typed-agent dispatch skill suite for Claude Code — plan, build, review, overnight unattended builds with work-item tracking and TDD enforcement.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "LIFEAI",
|
package/bin/rdc-skills-mcp.mjs
CHANGED
|
@@ -125,6 +125,36 @@ function textResult(text) {
|
|
|
125
125
|
return { content: [{ type: 'text', text }] };
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
function directMcpUsage(message) {
|
|
129
|
+
return {
|
|
130
|
+
jsonrpc: '2.0',
|
|
131
|
+
error: {
|
|
132
|
+
code: -32000,
|
|
133
|
+
message,
|
|
134
|
+
help: {
|
|
135
|
+
endpoint: '/mcp',
|
|
136
|
+
method: 'POST',
|
|
137
|
+
headers: {
|
|
138
|
+
'Content-Type': 'application/json',
|
|
139
|
+
Accept: 'application/json, text/event-stream',
|
|
140
|
+
},
|
|
141
|
+
tools: ['rdc_skill_list', 'rdc_skill_search', 'rdc_skill_get'],
|
|
142
|
+
response: 'Streamable HTTP returns Server-Sent Events. Parse the JSON-RPC envelope from each data: line; tool text is at result.content[0].text.',
|
|
143
|
+
curl: `curl -s -X POST https://rdc-skills.regendevcorp.com/mcp \\
|
|
144
|
+
-H 'Content-Type: application/json' \\
|
|
145
|
+
-H 'Accept: application/json, text/event-stream' \\
|
|
146
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"rdc_skill_list","arguments":{}}}'`,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
id: null,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function acceptsStreamableHttp(req) {
|
|
154
|
+
const accept = String(req.headers.accept || '');
|
|
155
|
+
return accept.includes('application/json') && accept.includes('text/event-stream');
|
|
156
|
+
}
|
|
157
|
+
|
|
128
158
|
function buildMcpServer() {
|
|
129
159
|
const srv = new McpServer({ name: 'rdc-skills', version: pkgVersion() });
|
|
130
160
|
|
|
@@ -225,6 +255,10 @@ function startHttp() {
|
|
|
225
255
|
// POST /mcp — stateless StreamableHTTP transport, no Authorization required.
|
|
226
256
|
app.post('/mcp', async (req, res) => {
|
|
227
257
|
try {
|
|
258
|
+
if (!acceptsStreamableHttp(req)) {
|
|
259
|
+
res.status(406).json(directMcpUsage('MCP Streamable HTTP requires Accept: application/json, text/event-stream'));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
228
262
|
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
|
|
229
263
|
const srv = buildMcpServer();
|
|
230
264
|
await srv.connect(transport);
|
|
@@ -246,11 +280,7 @@ function startHttp() {
|
|
|
246
280
|
});
|
|
247
281
|
|
|
248
282
|
app.get('/mcp', (_req, res) => {
|
|
249
|
-
res.status(405).json(
|
|
250
|
-
jsonrpc: '2.0',
|
|
251
|
-
error: { code: -32000, message: 'Use POST for MCP requests' },
|
|
252
|
-
id: null,
|
|
253
|
-
});
|
|
283
|
+
res.status(405).json(directMcpUsage('Use POST for MCP requests.'));
|
|
254
284
|
});
|
|
255
285
|
|
|
256
286
|
app.listen(PORT, () => {
|
package/git-sha.json
CHANGED
package/package.json
CHANGED
|
@@ -944,7 +944,9 @@ function listCommands() {
|
|
|
944
944
|
if (!fs.existsSync(cmdsDir)) return;
|
|
945
945
|
const files = fs.readdirSync(cmdsDir).filter(f => f.endsWith('.md')).sort();
|
|
946
946
|
const plugin = readJson(path.join(repoRoot, '.claude-plugin', 'plugin.json'), {});
|
|
947
|
-
const skillCount = Array.isArray(plugin.skills_meta)
|
|
947
|
+
const skillCount = Array.isArray(plugin.skills_meta)
|
|
948
|
+
? plugin.skills_meta.length
|
|
949
|
+
: (plugin.skills_meta && typeof plugin.skills_meta === 'object' ? Object.keys(plugin.skills_meta).length : null);
|
|
948
950
|
console.log('');
|
|
949
951
|
if (skillCount !== null) {
|
|
950
952
|
console.log(` \x1b[32mAvailable MCP skills (${skillCount}) and /rdc:* command shorthands (${files.length}):\x1b[0m`);
|
|
@@ -67,6 +67,16 @@ function curl(args, label) {
|
|
|
67
67
|
return { status: res.status, stdout, stderr };
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
function curlWithStatus(args, label) {
|
|
71
|
+
const res = curl([...args, '-w', '\n%{http_code}'], label);
|
|
72
|
+
const marker = res.stdout.match(/\n(\d{3})$/);
|
|
73
|
+
return {
|
|
74
|
+
...res,
|
|
75
|
+
httpStatus: marker ? Number(marker[1]) : null,
|
|
76
|
+
body: marker ? res.stdout.slice(0, marker.index) : res.stdout,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
70
80
|
function postMcp(payload, label) {
|
|
71
81
|
return curl([
|
|
72
82
|
'-s',
|
|
@@ -191,7 +201,19 @@ async function main() {
|
|
|
191
201
|
check('rdc_skill_get returns tool text path', getText.length > 500, `length ${getText.length}`);
|
|
192
202
|
check('rdc_skill_get accepts visible rdc:build alias', /rdc:build/i.test(getText));
|
|
193
203
|
|
|
194
|
-
const
|
|
204
|
+
const getMcp = curlWithStatus([
|
|
205
|
+
'-s',
|
|
206
|
+
`${TARGET}/mcp`,
|
|
207
|
+
], 'get_mcp_help');
|
|
208
|
+
check('GET /mcp curl exits 0', getMcp.status === 0, getMcp.stderr);
|
|
209
|
+
check('GET /mcp returns 405', getMcp.httpStatus === 405, `status ${getMcp.httpStatus}`);
|
|
210
|
+
const getHelp = JSON.parse(getMcp.body);
|
|
211
|
+
check('GET /mcp explains POST method', getHelp.error?.help?.method === 'POST');
|
|
212
|
+
check('GET /mcp includes Streamable HTTP Accept header', getHelp.error?.help?.headers?.Accept === 'application/json, text/event-stream');
|
|
213
|
+
check('GET /mcp points to discovery tools', getHelp.error?.help?.tools?.includes('rdc_skill_list'));
|
|
214
|
+
check('GET /mcp explains result.content text path', /result\.content\[0\]\.text/.test(getHelp.error?.help?.response || ''));
|
|
215
|
+
|
|
216
|
+
const badAccept = curlWithStatus([
|
|
195
217
|
'-s',
|
|
196
218
|
'-X', 'POST',
|
|
197
219
|
`${TARGET}/mcp`,
|
|
@@ -199,7 +221,10 @@ async function main() {
|
|
|
199
221
|
'-d', JSON.stringify({ jsonrpc: '2.0', id: 5, method: 'tools/list' }),
|
|
200
222
|
], 'missing_accept_header');
|
|
201
223
|
check('missing Accept header still returns inspectable response', badAccept.status === 0, badAccept.stderr);
|
|
202
|
-
check('missing Accept header
|
|
224
|
+
check('missing Accept header returns 406', badAccept.httpStatus === 406, `status ${badAccept.httpStatus}`);
|
|
225
|
+
const badAcceptHelp = JSON.parse(badAccept.body);
|
|
226
|
+
check('missing Accept header names required header', badAcceptHelp.error?.help?.headers?.Accept === 'application/json, text/event-stream');
|
|
227
|
+
check('missing Accept header includes curl example', /curl -s -X POST/.test(badAcceptHelp.error?.help?.curl || ''));
|
|
203
228
|
} finally {
|
|
204
229
|
if (proc) proc.kill();
|
|
205
230
|
}
|
|
@@ -20,11 +20,21 @@ assert.equal(toml.status, 0, `${toml.stdout}\n${toml.stderr}`);
|
|
|
20
20
|
assert.match(toml.stdout, /PASS/);
|
|
21
21
|
|
|
22
22
|
const source = readFileSync(script, 'utf8');
|
|
23
|
+
const plugin = JSON.parse(readFileSync(join(REPO_ROOT, '.claude-plugin', 'plugin.json'), 'utf8'));
|
|
24
|
+
const skillCount = Array.isArray(plugin.skills_meta)
|
|
25
|
+
? plugin.skills_meta.length
|
|
26
|
+
: Object.keys(plugin.skills_meta || {}).length;
|
|
27
|
+
assert.equal(skillCount, 29, 'test fixture should expose all 29 MCP skills from plugin skills_meta');
|
|
23
28
|
assert.match(
|
|
24
29
|
source,
|
|
25
30
|
/Available MCP skills.*\/rdc:\* command shorthands/,
|
|
26
31
|
'installer should distinguish the full MCP skill catalog from slash-command shorthands',
|
|
27
32
|
);
|
|
33
|
+
assert.match(
|
|
34
|
+
source,
|
|
35
|
+
/Object\.keys\(plugin\.skills_meta\)\.length/,
|
|
36
|
+
'installer should count object-shaped skills_meta manifests',
|
|
37
|
+
);
|
|
28
38
|
assert.match(
|
|
29
39
|
source,
|
|
30
40
|
/rdc_skill_list, rdc_skill_search, and rdc_skill_get/,
|