@mcpskillsio/server 1.0.1 → 2.0.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/index.js +207 -2
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -68,6 +68,32 @@ function formatDimensions(dims) {
|
|
|
68
68
|
].join("\n");
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
function formatRecommendations(recs) {
|
|
72
|
+
if (!recs || (!recs.alternatives?.length && !recs.crossCategory?.length)) return [];
|
|
73
|
+
|
|
74
|
+
const lines = ["", "## Recommendations"];
|
|
75
|
+
|
|
76
|
+
if (recs.alternatives?.length > 0) {
|
|
77
|
+
lines.push("");
|
|
78
|
+
lines.push(`**Alternatives in ${recs.categories?.[0]?.name || "this category"}:**`);
|
|
79
|
+
for (const alt of recs.alternatives) {
|
|
80
|
+
const scoreStr = alt.score ? ` — ${alt.score}/10 ${alt.tier ? formatTier(alt.tier) : ""}` : " — not yet scored";
|
|
81
|
+
lines.push(` → **${alt.repo}** (${alt.label})${scoreStr}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (recs.crossCategory?.length > 0) {
|
|
86
|
+
lines.push("");
|
|
87
|
+
lines.push("**You might also need:**");
|
|
88
|
+
for (const sug of recs.crossCategory) {
|
|
89
|
+
const scoreStr = sug.score ? ` (${sug.score}/10)` : "";
|
|
90
|
+
lines.push(` → **${sug.repo}**${scoreStr} — ${sug.label} (${sug.reason})`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return lines;
|
|
95
|
+
}
|
|
96
|
+
|
|
71
97
|
function formatFreeResult(data) {
|
|
72
98
|
const lines = [
|
|
73
99
|
`# Trust Score: ${data.repo}`,
|
|
@@ -88,10 +114,15 @@ function formatFreeResult(data) {
|
|
|
88
114
|
lines.push(` Safety: ${data.skill.safety?.summary || "Not scanned"}`);
|
|
89
115
|
}
|
|
90
116
|
|
|
117
|
+
// Recommendations
|
|
118
|
+
if (data.recommendations) {
|
|
119
|
+
lines.push(...formatRecommendations(data.recommendations));
|
|
120
|
+
}
|
|
121
|
+
|
|
91
122
|
lines.push(
|
|
92
123
|
"",
|
|
93
124
|
"---",
|
|
94
|
-
"🔒 Full report (all
|
|
125
|
+
"🔒 Full report (all signals + safety findings) → https://mcpskills.io",
|
|
95
126
|
"Set MCPSKILLS_API_KEY for full reports in-context."
|
|
96
127
|
);
|
|
97
128
|
|
|
@@ -118,6 +149,8 @@ function formatFullResult(data) {
|
|
|
118
149
|
issue_responsiveness: "Issue Response",
|
|
119
150
|
author_credibility: "Author Credibility",
|
|
120
151
|
community_adoption: "Community Adoption",
|
|
152
|
+
contributor_diversity: "Contributor Diversity",
|
|
153
|
+
download_adoption: "Download Adoption",
|
|
121
154
|
security_posture: "Security Posture",
|
|
122
155
|
dependency_health: "Dependency Health",
|
|
123
156
|
readme_quality: "README Quality",
|
|
@@ -125,6 +158,7 @@ function formatFullResult(data) {
|
|
|
125
158
|
skill_spec_compliance: "Spec Compliance",
|
|
126
159
|
license_clarity: "License Clarity",
|
|
127
160
|
tool_safety: "Tool Safety",
|
|
161
|
+
supply_chain_safety: "Supply Chain Safety",
|
|
128
162
|
};
|
|
129
163
|
|
|
130
164
|
for (const [key, val] of Object.entries(data.signals)) {
|
|
@@ -167,6 +201,11 @@ function formatFullResult(data) {
|
|
|
167
201
|
lines.push("", `## ⚠️ Disqualifiers: ${data.disqualifiers.join(", ")}`);
|
|
168
202
|
}
|
|
169
203
|
|
|
204
|
+
// Recommendations
|
|
205
|
+
if (data.recommendations) {
|
|
206
|
+
lines.push(...formatRecommendations(data.recommendations));
|
|
207
|
+
}
|
|
208
|
+
|
|
170
209
|
lines.push("", `Scanned at: ${data.scannedAt}`);
|
|
171
210
|
lines.push("Powered by mcpskills.io");
|
|
172
211
|
|
|
@@ -239,7 +278,7 @@ function formatSafetyResult(data) {
|
|
|
239
278
|
const server = new Server(
|
|
240
279
|
{
|
|
241
280
|
name: "mcpskills",
|
|
242
|
-
version: "
|
|
281
|
+
version: "2.0.0",
|
|
243
282
|
},
|
|
244
283
|
{
|
|
245
284
|
capabilities: {
|
|
@@ -299,6 +338,55 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
299
338
|
},
|
|
300
339
|
},
|
|
301
340
|
},
|
|
341
|
+
{
|
|
342
|
+
name: "get_badge",
|
|
343
|
+
description:
|
|
344
|
+
"Get a trust badge URL for any GitHub repo. Returns a shields.io-style SVG badge showing the trust score and tier. Embed in READMEs to show verified trust status. Badge auto-updates hourly.",
|
|
345
|
+
inputSchema: {
|
|
346
|
+
type: "object",
|
|
347
|
+
properties: {
|
|
348
|
+
repo: {
|
|
349
|
+
type: "string",
|
|
350
|
+
description: 'GitHub repo in "owner/repo" format',
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
required: ["repo"],
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
name: "watch_repo",
|
|
358
|
+
description:
|
|
359
|
+
"Start monitoring a repo for trust score changes. You'll be alerted when a repo's score changes significantly (±0.3 points or tier change). Free tier: up to 20 repos.",
|
|
360
|
+
inputSchema: {
|
|
361
|
+
type: "object",
|
|
362
|
+
properties: {
|
|
363
|
+
repo: {
|
|
364
|
+
type: "string",
|
|
365
|
+
description: 'GitHub repo in "owner/repo" format',
|
|
366
|
+
},
|
|
367
|
+
email: {
|
|
368
|
+
type: "string",
|
|
369
|
+
description: "Email address for alerts",
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
required: ["repo", "email"],
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
name: "check_watched",
|
|
377
|
+
description:
|
|
378
|
+
"Re-scan all watched repos and check for score changes. Returns any repos whose trust score changed significantly since last check.",
|
|
379
|
+
inputSchema: {
|
|
380
|
+
type: "object",
|
|
381
|
+
properties: {
|
|
382
|
+
email: {
|
|
383
|
+
type: "string",
|
|
384
|
+
description: "Email address used when watching repos",
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
required: ["email"],
|
|
388
|
+
},
|
|
389
|
+
},
|
|
302
390
|
],
|
|
303
391
|
};
|
|
304
392
|
});
|
|
@@ -394,6 +482,123 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
394
482
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
395
483
|
}
|
|
396
484
|
|
|
485
|
+
case "get_badge": {
|
|
486
|
+
const repo = args.repo;
|
|
487
|
+
if (!repo || !repo.includes("/")) {
|
|
488
|
+
return {
|
|
489
|
+
content: [{ type: "text", text: 'Invalid repo format. Use "owner/repo".' }],
|
|
490
|
+
isError: true,
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const badgeUrl = `https://mcpskills.io/.netlify/functions/badge?repo=${encodeURIComponent(repo)}`;
|
|
495
|
+
const reportUrl = `https://mcpskills.io/?repo=${encodeURIComponent(repo)}`;
|
|
496
|
+
|
|
497
|
+
const lines = [
|
|
498
|
+
`# Trust Badge for ${repo}`,
|
|
499
|
+
"",
|
|
500
|
+
"## Badge URL",
|
|
501
|
+
`\`${badgeUrl}\``,
|
|
502
|
+
"",
|
|
503
|
+
"## Embed in README.md",
|
|
504
|
+
"```markdown",
|
|
505
|
+
`[](${reportUrl})`,
|
|
506
|
+
"```",
|
|
507
|
+
"",
|
|
508
|
+
"## Embed in HTML",
|
|
509
|
+
"```html",
|
|
510
|
+
`<a href="${reportUrl}"><img src="${badgeUrl}" alt="MCP Skills Trust Score" /></a>`,
|
|
511
|
+
"```",
|
|
512
|
+
"",
|
|
513
|
+
"The badge auto-updates hourly. It shows the current trust score, tier, and links to the full report.",
|
|
514
|
+
"",
|
|
515
|
+
"---",
|
|
516
|
+
"Verified by mcpskills.io",
|
|
517
|
+
];
|
|
518
|
+
|
|
519
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
case "watch_repo": {
|
|
523
|
+
const repo = args.repo;
|
|
524
|
+
const email = args.email;
|
|
525
|
+
if (!repo || !repo.includes("/")) {
|
|
526
|
+
return {
|
|
527
|
+
content: [{ type: "text", text: 'Invalid repo format. Use "owner/repo".' }],
|
|
528
|
+
isError: true,
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
if (!email || !email.includes("@")) {
|
|
532
|
+
return {
|
|
533
|
+
content: [{ type: "text", text: "Invalid email address." }],
|
|
534
|
+
isError: true,
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const res = await fetch(`${API_BASE}/monitor`, {
|
|
539
|
+
method: "POST",
|
|
540
|
+
headers: { "Content-Type": "application/json" },
|
|
541
|
+
body: JSON.stringify({ action: "watch", repo, email }),
|
|
542
|
+
});
|
|
543
|
+
const data = await res.json();
|
|
544
|
+
|
|
545
|
+
const lines = [
|
|
546
|
+
`# Watching ${repo}`,
|
|
547
|
+
"",
|
|
548
|
+
`**Current Score:** ${data.currentScore ?? "Scanning..."}`,
|
|
549
|
+
`**Current Tier:** ${data.currentTier ? formatTier(data.currentTier) : "Unknown"}`,
|
|
550
|
+
`**Total Watched:** ${data.totalWatched || 1}`,
|
|
551
|
+
"",
|
|
552
|
+
`You'll be notified at ${email} when this repo's trust score changes by ±0.3 points or its tier changes.`,
|
|
553
|
+
"",
|
|
554
|
+
"Use **check_watched** to manually re-scan all your watched repos.",
|
|
555
|
+
];
|
|
556
|
+
|
|
557
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
case "check_watched": {
|
|
561
|
+
const email = args.email;
|
|
562
|
+
if (!email || !email.includes("@")) {
|
|
563
|
+
return {
|
|
564
|
+
content: [{ type: "text", text: "Invalid email address." }],
|
|
565
|
+
isError: true,
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
const res = await fetch(`${API_BASE}/monitor`, {
|
|
570
|
+
method: "POST",
|
|
571
|
+
headers: { "Content-Type": "application/json" },
|
|
572
|
+
body: JSON.stringify({ action: "check", email }),
|
|
573
|
+
});
|
|
574
|
+
const data = await res.json();
|
|
575
|
+
|
|
576
|
+
const lines = [
|
|
577
|
+
`# Monitoring Report`,
|
|
578
|
+
"",
|
|
579
|
+
`**Repos Checked:** ${data.checked || 0}`,
|
|
580
|
+
`**Changes Detected:** ${data.changes?.length || 0}`,
|
|
581
|
+
"",
|
|
582
|
+
];
|
|
583
|
+
|
|
584
|
+
if (data.changes?.length > 0) {
|
|
585
|
+
lines.push("## Score Changes");
|
|
586
|
+
for (const c of data.changes) {
|
|
587
|
+
const arrow = c.delta > 0 ? "↑" : "↓";
|
|
588
|
+
const emoji = c.delta > 0 ? "📈" : "📉";
|
|
589
|
+
lines.push(` ${emoji} **${c.repo}** ${c.oldScore} → ${c.newScore} (${arrow}${Math.abs(c.delta)})`);
|
|
590
|
+
if (c.tierChanged) {
|
|
591
|
+
lines.push(` Tier: ${c.oldTier} → ${c.newTier}`);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
} else {
|
|
595
|
+
lines.push("✅ All watched repos are stable. No significant changes.");
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
lines.push("", "---", "Powered by mcpskills.io");
|
|
599
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
600
|
+
}
|
|
601
|
+
|
|
397
602
|
default:
|
|
398
603
|
return {
|
|
399
604
|
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcpskillsio/server",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Trust-score any AI skill or MCP server from inside Claude Code, Cursor, or any MCP client.
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Trust-score any AI skill or MCP server from inside Claude Code, Cursor, or any MCP client. 14 signals, safety scanning, recommendations, badges, and monitoring.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"bin": {
|