@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.
- package/README.md +95 -0
- package/index.js +423 -0
- 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
|
+
}
|