@mcpskillsio/server 2.1.0 → 2.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.
Files changed (2) hide show
  1. package/index.js +80 -28
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -115,6 +115,55 @@ function formatRecommendations(recs) {
115
115
 
116
116
  // --- Agent Response Formatting ---
117
117
 
118
+ function formatPartialResult(data) {
119
+ const lines = [
120
+ `# Limited Trust Score: ${data.package || 'Unknown Package'}`,
121
+ "",
122
+ `⚠️ **Limited Score — No Source Repo Found**`,
123
+ `Score based on npm registry metadata only (${data.signalCount}/${data.totalPossibleSignals} signals).`,
124
+ "",
125
+ `**Score:** ${data.composite}/10`,
126
+ `**Tier:** ${formatTier(data.tier)}`,
127
+ "",
128
+ "## Dimensions (limited)",
129
+ formatDimensions(data.dimensions),
130
+ "",
131
+ "## Signals (npm metadata only)",
132
+ ];
133
+
134
+ const signalLabels = {
135
+ publish_recency: "Publish Recency",
136
+ publish_cadence: "Publish Cadence",
137
+ download_adoption: "Download Adoption",
138
+ maintainer_count: "Maintainer Count",
139
+ package_age: "Package Age",
140
+ dependency_count: "Dependency Count",
141
+ license_clarity: "License Clarity",
142
+ };
143
+
144
+ for (const [key, val] of Object.entries(data.signals || {})) {
145
+ const label = signalLabels[key] || key;
146
+ const v = typeof val === 'number' && !isNaN(val) ? val : 0;
147
+ const bar = "█".repeat(Math.round(v)) + "░".repeat(10 - Math.round(v));
148
+ lines.push(` ${bar} ${v}/10 ${label}`);
149
+ }
150
+
151
+ lines.push(
152
+ "",
153
+ `⚠️ ${data.limitedReason}`,
154
+ "",
155
+ `📦 ${data.meta?.versions || '?'} versions | 👥 ${data.meta?.maintainerCount || '?'} maintainers | 📄 ${data.meta?.license || 'Unknown'}`,
156
+ `📥 ${(data.meta?.npmDownloads || 0).toLocaleString()} downloads/month`,
157
+ );
158
+
159
+ if (data.meta?.homepage) {
160
+ lines.push(`🔗 ${data.meta.homepage}`);
161
+ }
162
+
163
+ lines.push("", `Scanned at: ${data.scannedAt}`, "Powered by mcpskills.io");
164
+ return lines.join("\n");
165
+ }
166
+
118
167
  function formatAgentResponse(data) {
119
168
  // Compact agent response (from free tier API)
120
169
  const rec = data.recommendation || (data.safe ? 'install' : 'caution');
@@ -308,7 +357,7 @@ function formatSafetyResult(data) {
308
357
  const server = new Server(
309
358
  {
310
359
  name: "mcpskills",
311
- version: "2.1.0",
360
+ version: "2.2.0",
312
361
  },
313
362
  {
314
363
  capabilities: {
@@ -324,14 +373,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
324
373
  {
325
374
  name: "check_trust_score",
326
375
  description:
327
- "Score any GitHub repo for trustworthiness. Returns a trust score (0-10) across 4 dimensions: Alive (maintained?), Legit (credible author?), Solid (secure?), Usable (good docs?). AI skills and MCP servers get enhanced scanning with 5 safety checks. Set MCPSKILLS_API_KEY env var for full 14-signal reports.",
376
+ "Score any AI skill, MCP server, or GitHub repo for trustworthiness. Returns a trust score (0-10) across 4 dimensions: Alive, Legit, Solid, Usable. Accepts: owner/repo, GitHub URL, npm package (npm:@scope/name or @scope/name), Smithery URL, or OpenClaw URL. AI skills get enhanced safety scanning. Set MCPSKILLS_API_KEY for full reports.",
328
377
  inputSchema: {
329
378
  type: "object",
330
379
  properties: {
331
380
  repo: {
332
381
  type: "string",
333
382
  description:
334
- 'GitHub repo in "owner/repo" format (e.g., "anthropics/anthropic-sdk-typescript")',
383
+ 'Any of: "owner/repo", GitHub URL, "npm:@scope/package", "@scope/package", Smithery URL, or OpenClaw URL',
335
384
  },
336
385
  },
337
386
  required: ["repo"],
@@ -340,14 +389,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
340
389
  {
341
390
  name: "scan_safety",
342
391
  description:
343
- "Run a focused safety scan on an AI skill or MCP server repo. Checks for prompt injection, shell execution, network exfiltration, credential theft, and obfuscated payloads. Returns detailed findings with file locations. Only works on repos detected as AI skills.",
392
+ "Run a focused safety scan on an AI skill or MCP server. Checks for prompt injection, shell execution, network exfiltration, credential theft, and obfuscated payloads. Accepts any input format (owner/repo, npm package, Smithery URL, etc.).",
344
393
  inputSchema: {
345
394
  type: "object",
346
395
  properties: {
347
396
  repo: {
348
397
  type: "string",
349
398
  description:
350
- 'GitHub repo in "owner/repo" format (e.g., "modelcontextprotocol/servers")',
399
+ 'Any of: "owner/repo", GitHub URL, "npm:@scope/package", Smithery URL, or OpenClaw URL',
351
400
  },
352
401
  },
353
402
  required: ["repo"],
@@ -371,13 +420,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
371
420
  {
372
421
  name: "get_badge",
373
422
  description:
374
- "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.",
423
+ "Get a trust badge URL for any repo or package. Returns a shields.io-style SVG badge showing the trust score and tier. Embed in READMEs. Badge auto-updates hourly.",
375
424
  inputSchema: {
376
425
  type: "object",
377
426
  properties: {
378
427
  repo: {
379
428
  type: "string",
380
- description: 'GitHub repo in "owner/repo" format',
429
+ description: 'Any of: "owner/repo", GitHub URL, "npm:@scope/package", or Smithery URL',
381
430
  },
382
431
  },
383
432
  required: ["repo"],
@@ -386,13 +435,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
386
435
  {
387
436
  name: "watch_repo",
388
437
  description:
389
- "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). Requires a paid API key.",
438
+ "Start monitoring a repo or package for trust score changes. Alerts when score changes significantly (±0.3 points or tier change). Requires a paid API key.",
390
439
  inputSchema: {
391
440
  type: "object",
392
441
  properties: {
393
442
  repo: {
394
443
  type: "string",
395
- description: 'GitHub repo in "owner/repo" format',
444
+ description: 'Any of: "owner/repo", GitHub URL, "npm:@scope/package", or Smithery URL',
396
445
  },
397
446
  email: {
398
447
  type: "string",
@@ -420,14 +469,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
420
469
  {
421
470
  name: "batch_check",
422
471
  description:
423
- "Check up to 5 repos in one call. Returns a trust assessment for each repo. Requires a Pro API key (MCPSKILLS_API_KEY). Great for bulk-vetting dependencies.",
472
+ "Check up to 5 repos or packages in one call. Returns a trust assessment for each. Requires a Pro API key. Accepts any mix of formats (owner/repo, npm packages, registry URLs).",
424
473
  inputSchema: {
425
474
  type: "object",
426
475
  properties: {
427
476
  repos: {
428
477
  type: "array",
429
478
  items: { type: "string" },
430
- description: 'Array of GitHub repos in "owner/repo" format (max 5)',
479
+ description: 'Array of repos/packages in any format (max 5). E.g., ["owner/repo", "npm:@scope/pkg", "https://smithery.ai/server/name"]',
431
480
  },
432
481
  },
433
482
  required: ["repos"],
@@ -436,13 +485,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
436
485
  {
437
486
  name: "auto_gate",
438
487
  description:
439
- 'Should I install this? Returns a simple go/no-go decision with reasoning. The fastest way to check if a repo is safe to use. Returns { proceed: true/false, reason: "..." }.',
488
+ 'Should I install this? Returns a simple go/no-go decision with reasoning. Accepts any format: owner/repo, npm package, Smithery URL, etc. Returns { proceed: true/false, reason: "..." }.',
440
489
  inputSchema: {
441
490
  type: "object",
442
491
  properties: {
443
492
  repo: {
444
493
  type: "string",
445
- description: 'GitHub repo in "owner/repo" format',
494
+ description: 'Any of: "owner/repo", "npm:@scope/package", "@scope/package", Smithery URL, or OpenClaw URL',
446
495
  },
447
496
  },
448
497
  required: ["repo"],
@@ -461,12 +510,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
461
510
  switch (name) {
462
511
  case "check_trust_score": {
463
512
  const repo = args.repo;
464
- if (!repo || !repo.includes("/")) {
513
+ if (!repo || repo.trim().length === 0) {
465
514
  return {
466
515
  content: [
467
516
  {
468
517
  type: "text",
469
- text: 'Invalid repo format. Use "owner/repo" (e.g., "anthropics/anthropic-sdk-typescript").',
518
+ text: 'Missing input. Accepts: "owner/repo", npm package, GitHub URL, Smithery URL, or OpenClaw URL.',
470
519
  },
471
520
  ],
472
521
  isError: true,
@@ -475,9 +524,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
475
524
 
476
525
  const data = await fetchScore(repo, apiKey);
477
526
 
478
- // Determine if we got a full response or agent compact response
527
+ // Determine if we got a full response, partial, or agent compact response
479
528
  let formatted;
480
- if (data.signals && data.dimensions) {
529
+ if (data.limited || data.mode === 'partial') {
530
+ // Partial score — no source repo found
531
+ formatted = formatPartialResult(data);
532
+ } else if (data.signals && data.dimensions) {
481
533
  // Full paid response
482
534
  formatted = formatFullResult(data);
483
535
  } else if (data.safe !== undefined) {
@@ -485,7 +537,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
485
537
  formatted = formatAgentResponse(data);
486
538
  } else {
487
539
  // Human free response — format it for the agent
488
- formatted = `${formatTier(data.tier)} ${data.repo} — ${data.composite}/10\n\nSet MCPSKILLS_API_KEY for full signal breakdown.`;
540
+ formatted = `${formatTier(data.tier)} ${data.repo || data.package} — ${data.composite}/10\n\nSet MCPSKILLS_API_KEY for full signal breakdown.`;
489
541
  }
490
542
 
491
543
  return { content: [{ type: "text", text: formatted }] };
@@ -493,12 +545,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
493
545
 
494
546
  case "scan_safety": {
495
547
  const repo = args.repo;
496
- if (!repo || !repo.includes("/")) {
548
+ if (!repo || repo.trim().length === 0) {
497
549
  return {
498
550
  content: [
499
551
  {
500
552
  type: "text",
501
- text: 'Invalid repo format. Use "owner/repo".',
553
+ text: 'Missing input. Accepts: "owner/repo", npm package, GitHub URL, Smithery URL, or OpenClaw URL.',
502
554
  },
503
555
  ],
504
556
  isError: true,
@@ -555,9 +607,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
555
607
 
556
608
  case "get_badge": {
557
609
  const repo = args.repo;
558
- if (!repo || !repo.includes("/")) {
610
+ if (!repo || repo.trim().length === 0) {
559
611
  return {
560
- content: [{ type: "text", text: 'Invalid repo format. Use "owner/repo".' }],
612
+ content: [{ type: "text", text: 'Missing input. Accepts: "owner/repo", npm package, or registry URL.' }],
561
613
  isError: true,
562
614
  };
563
615
  }
@@ -593,9 +645,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
593
645
  case "watch_repo": {
594
646
  const repo = args.repo;
595
647
  const email = args.email;
596
- if (!repo || !repo.includes("/")) {
648
+ if (!repo || repo.trim().length === 0) {
597
649
  return {
598
- content: [{ type: "text", text: 'Invalid repo format. Use "owner/repo".' }],
650
+ content: [{ type: "text", text: 'Missing input. Accepts: "owner/repo", npm package, or registry URL.' }],
599
651
  isError: true,
600
652
  };
601
653
  }
@@ -703,8 +755,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
703
755
  const results = [];
704
756
 
705
757
  for (const repo of batch) {
706
- if (!repo.includes("/")) {
707
- results.push(`❌ ${repo} — invalid format`);
758
+ if (!repo || repo.trim().length === 0) {
759
+ results.push(`❌ (empty) — invalid input`);
708
760
  continue;
709
761
  }
710
762
  try {
@@ -740,9 +792,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
740
792
 
741
793
  case "auto_gate": {
742
794
  const repo = args.repo;
743
- if (!repo || !repo.includes("/")) {
795
+ if (!repo || repo.trim().length === 0) {
744
796
  return {
745
- content: [{ type: "text", text: 'Invalid repo format. Use "owner/repo".' }],
797
+ content: [{ type: "text", text: 'Missing input. Accepts: "owner/repo", npm package, or registry URL.' }],
746
798
  isError: true,
747
799
  };
748
800
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mcpskillsio/server",
3
- "version": "2.1.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, monitoring, batch checking, and auto-gate decisions.",
3
+ "version": "2.2.0",
4
+ "description": "Trust-score any AI skill or MCP server from inside Claude Code, Cursor, or any MCP client. Accepts GitHub repos, npm packages, Smithery URLs, and OpenClaw skills. 14 signals, safety scanning, recommendations, badges, monitoring, batch checking, and auto-gate decisions.",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "bin": {