@mcpskillsio/server 1.0.0 → 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.
Files changed (2) hide show
  1. package/index.js +215 -8
  2. package/package.json +3 -3
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 12 signals + safety findings) → https://mcpskills.io",
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: "1.0.0",
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
  });
@@ -352,7 +440,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
352
440
  }
353
441
 
354
442
  case "list_packages": {
355
- const packages = await fetchPackages();
443
+ const raw = await fetchPackages();
444
+ const packages = raw.packages || raw;
356
445
  const filter = args.package_name?.toLowerCase();
357
446
 
358
447
  let filtered = packages;
@@ -378,12 +467,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
378
467
  const lines = ["# MCP Skills — Curated Packages", ""];
379
468
  for (const pkg of filtered) {
380
469
  lines.push(`## ${pkg.name}`);
381
- lines.push(pkg.description);
470
+ lines.push(pkg.tagline || pkg.description || "");
382
471
  lines.push("");
383
- for (const skill of pkg.skills) {
384
- lines.push(
385
- ` - **${skill.name}** (${skill.repo}) — ${skill.description}`
386
- );
472
+ const items = pkg.skills || pkg.repos || [];
473
+ for (const item of items) {
474
+ const name = item.name || `${item.owner}/${item.repo}`;
475
+ const desc = item.description || item.role || "";
476
+ lines.push(` - **${name}** — ${desc}`);
387
477
  }
388
478
  lines.push("");
389
479
  }
@@ -392,6 +482,123 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
392
482
  return { content: [{ type: "text", text: lines.join("\n") }] };
393
483
  }
394
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
+ `[![MCP Skills Trust Score](${badgeUrl})](${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
+
395
602
  default:
396
603
  return {
397
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": "1.0.0",
4
- "description": "Trust-score any AI skill or MCP server from inside Claude Code, Cursor, or any MCP client. 12 signals across 4 dimensions with safety scanning.",
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": {
@@ -24,7 +24,7 @@
24
24
  "license": "MIT",
25
25
  "repository": {
26
26
  "type": "git",
27
- "url": "https://github.com/riseabovepartners/mcpskills-server"
27
+ "url": "https://github.com/bebravebekind/mcpskills-server"
28
28
  },
29
29
  "homepage": "https://mcpskills.io",
30
30
  "engines": {