@mcpskillsio/server 1.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 (3) hide show
  1. package/README.md +95 -0
  2. package/index.js +423 -0
  3. package/package.json +36 -0
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # @mcpskillsio/server
2
+
3
+ Trust-score any AI skill or MCP server from inside Claude Code, Cursor, or any MCP client.
4
+
5
+ 12 signals across 4 dimensions with safety scanning for prompt injection, credential theft, and supply chain attacks.
6
+
7
+ ## Install
8
+
9
+ ### Claude Code
10
+
11
+ ```bash
12
+ claude mcp add mcpskills -- npx @mcpskillsio/server
13
+ ```
14
+
15
+ ### Cursor
16
+
17
+ Add to your `.cursor/mcp.json`:
18
+
19
+ ```json
20
+ {
21
+ "mcpServers": {
22
+ "mcpskills": {
23
+ "command": "npx",
24
+ "args": ["@mcpskillsio/server"]
25
+ }
26
+ }
27
+ }
28
+ ```
29
+
30
+ ### Claude Desktop
31
+
32
+ Add to `claude_desktop_config.json`:
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "mcpskills": {
38
+ "command": "npx",
39
+ "args": ["@mcpskillsio/server"]
40
+ }
41
+ }
42
+ }
43
+ ```
44
+
45
+ ## Tools
46
+
47
+ ### `check_trust_score`
48
+
49
+ Score any GitHub repo. Returns trust tier, composite score, and 4 dimension scores.
50
+
51
+ ```
52
+ "Score anthropics/anthropic-sdk-typescript"
53
+ ```
54
+
55
+ ### `scan_safety`
56
+
57
+ Focused safety scan for AI skills. Checks for prompt injection, shell execution, network exfiltration, credential theft, and obfuscated payloads.
58
+
59
+ ```
60
+ "Is this MCP server safe? modelcontextprotocol/servers"
61
+ ```
62
+
63
+ ### `list_packages`
64
+
65
+ Browse curated, pre-scored skill packages organized by use case.
66
+
67
+ ```
68
+ "Show me safe AI skill packages for full-stack development"
69
+ ```
70
+
71
+ ## Full Reports
72
+
73
+ Free tier returns trust tier + dimension scores (same as mcpskills.io free scans).
74
+
75
+ For full 12-signal reports with detailed safety findings inside your IDE, set your API key:
76
+
77
+ ```bash
78
+ export MCPSKILLS_API_KEY=your_key_here
79
+ ```
80
+
81
+ Get your API key at [mcpskills.io/api](https://mcpskills.io/api).
82
+
83
+ ## How It Works
84
+
85
+ The server calls the mcpskills.io trust scoring API, which:
86
+
87
+ 1. Fetches repo data from GitHub API and OpenSSF Scorecard
88
+ 2. Scores 12 signals across 4 dimensions (Alive, Legit, Solid, Usable)
89
+ 3. Detects AI skills/MCP servers and activates Skills Mode
90
+ 4. Runs 5 safety scans based on ClawHavoc and ToxicSkills attack patterns
91
+ 5. Assigns a trust tier: Verified (≥7.5), Established (≥4.5), New, or Blocked
92
+
93
+ ## License
94
+
95
+ MIT — Built by [Michael Browne](https://linkedin.com/in/michaelbrowne03/) at [Rise Above Partners](https://rise-above.net).
package/index.js ADDED
@@ -0,0 +1,423 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MCP Skills — Trust Score Server
5
+ *
6
+ * An MCP server that provides AI skill and repo trust scoring
7
+ * directly inside Claude Code, Cursor, and any MCP client.
8
+ *
9
+ * Tools:
10
+ * - check_trust_score: Score any GitHub repo
11
+ * - scan_safety: Run safety-only scan for AI skills
12
+ * - list_packages: Browse curated safe skill packages
13
+ *
14
+ * Free tier returns tier + dimension scores.
15
+ * Full reports (all 12 signals + safety findings) require an API key.
16
+ * Get your key at https://mcpskills.io/api
17
+ */
18
+
19
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
20
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
21
+ import {
22
+ CallToolRequestSchema,
23
+ ListToolsRequestSchema,
24
+ } from "@modelcontextprotocol/sdk/types.js";
25
+
26
+ const API_BASE = "https://mcpskills.io/.netlify/functions";
27
+ const PACKAGES_URL = "https://mcpskills.io/.netlify/functions/packages";
28
+
29
+ // --- API Client ---
30
+
31
+ async function fetchScore(repo, apiKey) {
32
+ const headers = { "Content-Type": "application/json" };
33
+ if (apiKey) headers["X-API-Key"] = apiKey;
34
+
35
+ const res = await fetch(`${API_BASE}/score`, {
36
+ method: "POST",
37
+ headers,
38
+ body: JSON.stringify({ repo }),
39
+ });
40
+
41
+ if (!res.ok) {
42
+ const err = await res.json().catch(() => ({ error: res.statusText }));
43
+ throw new Error(err.error || `API error: ${res.status}`);
44
+ }
45
+
46
+ return res.json();
47
+ }
48
+
49
+ async function fetchPackages() {
50
+ const res = await fetch(PACKAGES_URL);
51
+ if (!res.ok) throw new Error("Could not fetch packages");
52
+ return res.json();
53
+ }
54
+
55
+ // --- Format Helpers ---
56
+
57
+ function formatTier(tier) {
58
+ const icons = { verified: "✅", established: "🟡", new: "⚪", blocked: "🚫" };
59
+ return `${icons[tier] || "❓"} ${tier.charAt(0).toUpperCase() + tier.slice(1)}`;
60
+ }
61
+
62
+ function formatDimensions(dims) {
63
+ return [
64
+ ` Alive: ${dims.alive}/10 (maintained?)`,
65
+ ` Legit: ${dims.legit}/10 (credible author?)`,
66
+ ` Solid: ${dims.solid}/10 (secure?)`,
67
+ ` Usable: ${dims.usable}/10 (good docs?)`,
68
+ ].join("\n");
69
+ }
70
+
71
+ function formatFreeResult(data) {
72
+ const lines = [
73
+ `# Trust Score: ${data.repo}`,
74
+ "",
75
+ `**Score:** ${data.composite}/10`,
76
+ `**Tier:** ${formatTier(data.tier)}`,
77
+ `**Mode:** ${data.mode === "skills" ? "Skills Mode (AI skill detected)" : "Standard Mode"}`,
78
+ "",
79
+ `## Dimensions`,
80
+ formatDimensions(data.dimensions),
81
+ "",
82
+ `⭐ ${data.meta.stars.toLocaleString()} stars | 🍴 ${data.meta.forks.toLocaleString()} forks | 📄 ${data.meta.license}`,
83
+ ];
84
+
85
+ if (data.mode === "skills" && data.skill) {
86
+ lines.push("", `## Skill Info`);
87
+ lines.push(` Type: ${data.skill.type}`);
88
+ lines.push(` Safety: ${data.skill.safety?.summary || "Not scanned"}`);
89
+ }
90
+
91
+ lines.push(
92
+ "",
93
+ "---",
94
+ "🔒 Full report (all 12 signals + safety findings) → https://mcpskills.io",
95
+ "Set MCPSKILLS_API_KEY for full reports in-context."
96
+ );
97
+
98
+ return lines.join("\n");
99
+ }
100
+
101
+ function formatFullResult(data) {
102
+ const lines = [
103
+ `# Trust Score: ${data.repo}`,
104
+ "",
105
+ `**Score:** ${data.composite}/10`,
106
+ `**Tier:** ${formatTier(data.tier)}`,
107
+ `**Mode:** ${data.mode === "skills" ? "Skills Mode (AI skill detected)" : "Standard Mode"}`,
108
+ "",
109
+ `## Dimensions`,
110
+ formatDimensions(data.dimensions),
111
+ "",
112
+ `## All Signals`,
113
+ ];
114
+
115
+ const signalLabels = {
116
+ commit_recency: "Commit Recency",
117
+ release_cadence: "Release Cadence",
118
+ issue_responsiveness: "Issue Response",
119
+ author_credibility: "Author Credibility",
120
+ community_adoption: "Community Adoption",
121
+ security_posture: "Security Posture",
122
+ dependency_health: "Dependency Health",
123
+ readme_quality: "README Quality",
124
+ file_structure: "File Structure",
125
+ skill_spec_compliance: "Spec Compliance",
126
+ license_clarity: "License Clarity",
127
+ tool_safety: "Tool Safety",
128
+ };
129
+
130
+ for (const [key, val] of Object.entries(data.signals)) {
131
+ const label = signalLabels[key] || key;
132
+ const bar = "█".repeat(Math.round(val)) + "░".repeat(10 - Math.round(val));
133
+ lines.push(` ${bar} ${val}/10 ${label}`);
134
+ }
135
+
136
+ lines.push(
137
+ "",
138
+ `⭐ ${data.meta.stars.toLocaleString()} stars | 🍴 ${data.meta.forks.toLocaleString()} forks | 📄 ${data.meta.license}`
139
+ );
140
+
141
+ if (data.mode === "skills" && data.skill) {
142
+ lines.push("", `## Skill Details`);
143
+ lines.push(` Type: ${data.skill.type}`);
144
+ lines.push(` Indicators: ${data.skill.indicators.join(", ")}`);
145
+
146
+ if (data.skill.specChecks) {
147
+ lines.push("", ` ### Spec Compliance`);
148
+ for (const [check, passed] of Object.entries(data.skill.specChecks)) {
149
+ lines.push(` ${passed ? "✅" : "❌"} ${check}`);
150
+ }
151
+ }
152
+
153
+ lines.push("", ` ### Safety Scan`);
154
+ lines.push(` Summary: ${data.skill.safety?.summary || "Not scanned"}`);
155
+ if (data.skill.safety?.findings?.length > 0) {
156
+ for (const f of data.skill.safety.findings) {
157
+ lines.push(
158
+ ` ⚠️ [${f.severity}] ${f.check} in ${f.file}: ${f.snippet}`
159
+ );
160
+ }
161
+ } else {
162
+ lines.push(" ✅ No threat patterns detected");
163
+ }
164
+ }
165
+
166
+ if (data.disqualifiers?.length > 0) {
167
+ lines.push("", `## ⚠️ Disqualifiers: ${data.disqualifiers.join(", ")}`);
168
+ }
169
+
170
+ lines.push("", `Scanned at: ${data.scannedAt}`);
171
+ lines.push("Powered by mcpskills.io");
172
+
173
+ return lines.join("\n");
174
+ }
175
+
176
+ function formatSafetyResult(data) {
177
+ if (data.mode !== "skills" || !data.skill) {
178
+ return [
179
+ `# Safety Scan: ${data.repo}`,
180
+ "",
181
+ "This repo was not detected as an AI skill or MCP server.",
182
+ "Safety scanning only runs on repos identified as skills.",
183
+ "",
184
+ `Mode: ${data.mode}`,
185
+ `Score: ${data.composite}/10 | Tier: ${formatTier(data.tier)}`,
186
+ ].join("\n");
187
+ }
188
+
189
+ const lines = [
190
+ `# Safety Scan: ${data.repo}`,
191
+ "",
192
+ `**Skill Type:** ${data.skill.type}`,
193
+ `**Safety Score:** ${data.signals.tool_safety}/10`,
194
+ `**Status:** ${data.skill.safety?.summary || "Unknown"}`,
195
+ "",
196
+ ];
197
+
198
+ if (data.skill.safety?.isBlocked) {
199
+ lines.push("🚫 **BLOCKED** — This skill has confirmed threat patterns and should NOT be installed.");
200
+ lines.push("");
201
+ }
202
+
203
+ lines.push("## Safety Checks");
204
+ const checks = [
205
+ "Prompt injection (hidden instructions in SKILL.md/README)",
206
+ "Shell execution (piped curl|sh, eval with network content)",
207
+ "Network exfiltration (Discord webhooks, raw IP C2, pastebin)",
208
+ "Credential access (SSH keys, AWS creds, browser storage)",
209
+ "Obfuscated payloads (base64, hex encoding, charCode chains)",
210
+ ];
211
+
212
+ if (data.skill.safety?.findings?.length > 0) {
213
+ const foundChecks = new Set(data.skill.safety.findings.map((f) => f.check));
214
+ for (const check of checks) {
215
+ const key = check.split(" (")[0].toLowerCase().replace(/ /g, "_");
216
+ const found = [...foundChecks].some((f) => f.includes(key.split("_")[0]));
217
+ lines.push(` ${found ? "⚠️" : "✅"} ${check}`);
218
+ }
219
+ lines.push("", "## Findings");
220
+ for (const f of data.skill.safety.findings) {
221
+ lines.push(` ⚠️ [${f.severity.toUpperCase()}] ${f.check}`);
222
+ lines.push(` File: ${f.file}`);
223
+ lines.push(` Match: ${f.snippet}`);
224
+ lines.push("");
225
+ }
226
+ } else {
227
+ for (const check of checks) {
228
+ lines.push(` ✅ ${check}`);
229
+ }
230
+ lines.push("", "✅ No threat patterns detected. Clean scan.");
231
+ }
232
+
233
+ lines.push("", "---", "Powered by mcpskills.io");
234
+ return lines.join("\n");
235
+ }
236
+
237
+ // --- MCP Server ---
238
+
239
+ const server = new Server(
240
+ {
241
+ name: "mcpskills",
242
+ version: "1.0.0",
243
+ },
244
+ {
245
+ capabilities: {
246
+ tools: {},
247
+ },
248
+ }
249
+ );
250
+
251
+ // List tools
252
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
253
+ return {
254
+ tools: [
255
+ {
256
+ name: "check_trust_score",
257
+ description:
258
+ "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 12-signal reports.",
259
+ inputSchema: {
260
+ type: "object",
261
+ properties: {
262
+ repo: {
263
+ type: "string",
264
+ description:
265
+ 'GitHub repo in "owner/repo" format (e.g., "anthropics/anthropic-sdk-typescript")',
266
+ },
267
+ },
268
+ required: ["repo"],
269
+ },
270
+ },
271
+ {
272
+ name: "scan_safety",
273
+ description:
274
+ "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.",
275
+ inputSchema: {
276
+ type: "object",
277
+ properties: {
278
+ repo: {
279
+ type: "string",
280
+ description:
281
+ 'GitHub repo in "owner/repo" format (e.g., "modelcontextprotocol/servers")',
282
+ },
283
+ },
284
+ required: ["repo"],
285
+ },
286
+ },
287
+ {
288
+ name: "list_packages",
289
+ description:
290
+ "Browse curated, pre-scored AI skill packages organized by use case. Each package contains vetted skills with trust scores. Available packages: Claude Power User, Full-Stack Vibe Coder, Data & Research, DevOps & Infrastructure, Content & Marketing.",
291
+ inputSchema: {
292
+ type: "object",
293
+ properties: {
294
+ package_name: {
295
+ type: "string",
296
+ description:
297
+ 'Optional: filter by package name (e.g., "claude-power-user"). Omit to list all packages.',
298
+ },
299
+ },
300
+ },
301
+ },
302
+ ],
303
+ };
304
+ });
305
+
306
+ // Handle tool calls
307
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
308
+ const { name, arguments: args } = request.params;
309
+ const apiKey = process.env.MCPSKILLS_API_KEY || null;
310
+
311
+ try {
312
+ switch (name) {
313
+ case "check_trust_score": {
314
+ const repo = args.repo;
315
+ if (!repo || !repo.includes("/")) {
316
+ return {
317
+ content: [
318
+ {
319
+ type: "text",
320
+ text: 'Invalid repo format. Use "owner/repo" (e.g., "anthropics/anthropic-sdk-typescript").',
321
+ },
322
+ ],
323
+ isError: true,
324
+ };
325
+ }
326
+
327
+ const data = await fetchScore(repo, apiKey);
328
+ const formatted = apiKey
329
+ ? formatFullResult(data)
330
+ : formatFreeResult(data);
331
+
332
+ return { content: [{ type: "text", text: formatted }] };
333
+ }
334
+
335
+ case "scan_safety": {
336
+ const repo = args.repo;
337
+ if (!repo || !repo.includes("/")) {
338
+ return {
339
+ content: [
340
+ {
341
+ type: "text",
342
+ text: 'Invalid repo format. Use "owner/repo".',
343
+ },
344
+ ],
345
+ isError: true,
346
+ };
347
+ }
348
+
349
+ const data = await fetchScore(repo, apiKey);
350
+ const formatted = formatSafetyResult(data);
351
+ return { content: [{ type: "text", text: formatted }] };
352
+ }
353
+
354
+ case "list_packages": {
355
+ const packages = await fetchPackages();
356
+ const filter = args.package_name?.toLowerCase();
357
+
358
+ let filtered = packages;
359
+ if (filter) {
360
+ filtered = packages.filter(
361
+ (p) =>
362
+ p.id.includes(filter) ||
363
+ p.name.toLowerCase().includes(filter)
364
+ );
365
+ }
366
+
367
+ if (filtered.length === 0) {
368
+ return {
369
+ content: [
370
+ {
371
+ type: "text",
372
+ text: `No packages found${filter ? ` matching "${filter}"` : ""}. Available: ${packages.map((p) => p.id).join(", ")}`,
373
+ },
374
+ ],
375
+ };
376
+ }
377
+
378
+ const lines = ["# MCP Skills — Curated Packages", ""];
379
+ for (const pkg of filtered) {
380
+ lines.push(`## ${pkg.name}`);
381
+ lines.push(pkg.description);
382
+ lines.push("");
383
+ for (const skill of pkg.skills) {
384
+ lines.push(
385
+ ` - **${skill.name}** (${skill.repo}) — ${skill.description}`
386
+ );
387
+ }
388
+ lines.push("");
389
+ }
390
+
391
+ lines.push("---", "All packages vetted by mcpskills.io");
392
+ return { content: [{ type: "text", text: lines.join("\n") }] };
393
+ }
394
+
395
+ default:
396
+ return {
397
+ content: [{ type: "text", text: `Unknown tool: ${name}` }],
398
+ isError: true,
399
+ };
400
+ }
401
+ } catch (err) {
402
+ return {
403
+ content: [
404
+ {
405
+ type: "text",
406
+ text: `Error: ${err.message}\n\nIf this persists, try scanning at https://mcpskills.io`,
407
+ },
408
+ ],
409
+ isError: true,
410
+ };
411
+ }
412
+ });
413
+
414
+ // Start server
415
+ async function main() {
416
+ const transport = new StdioServerTransport();
417
+ await server.connect(transport);
418
+ }
419
+
420
+ main().catch((err) => {
421
+ console.error("MCP Skills server failed to start:", err);
422
+ process.exit(1);
423
+ });
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
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.",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "mcpskills": "index.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node index.js"
12
+ },
13
+ "keywords": [
14
+ "mcp",
15
+ "mcp-server",
16
+ "trust-score",
17
+ "ai-skills",
18
+ "security",
19
+ "safety-scanner",
20
+ "claude-code",
21
+ "cursor"
22
+ ],
23
+ "author": "Michael Browne <hello@mcpskills.io> (https://mcpskills.io)",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/riseabovepartners/mcpskills-server"
28
+ },
29
+ "homepage": "https://mcpskills.io",
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ },
33
+ "dependencies": {
34
+ "@modelcontextprotocol/sdk": "^1.12.0"
35
+ }
36
+ }