@unfragile/mcp-server 0.1.0 → 0.1.1

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/dist/index.js +36 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -33,7 +33,7 @@ async function searchAPI(query, options = {}) {
33
33
  if (API_KEY)
34
34
  headers["X-API-Key"] = API_KEY;
35
35
  const controller = new AbortController();
36
- const timeout = setTimeout(() => controller.abort(), 10_000);
36
+ const timeout = setTimeout(() => controller.abort(), 15_000);
37
37
  try {
38
38
  const res = await fetch(`${API_BASE}/api/v1/search?${params}`, {
39
39
  headers,
@@ -54,11 +54,35 @@ async function searchAPI(query, options = {}) {
54
54
  }
55
55
  }
56
56
  // ─── Formatters ──────────────────────────────────────────────
57
+ /** Extract a readable name from a URL when artifact.name is a raw URL */
58
+ function cleanName(name, url) {
59
+ if (!name || /^https?:\/\//.test(name)) {
60
+ // Try to derive name from URL path
61
+ try {
62
+ const u = new URL(url || name);
63
+ const parts = u.pathname.split("/").filter(Boolean);
64
+ // GitHub: owner/repo → repo
65
+ if (u.hostname === "github.com" && parts.length >= 2) {
66
+ return parts[1].replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
67
+ }
68
+ // npm/PyPI: package name
69
+ if (parts.length > 0) {
70
+ const last = parts[parts.length - 1];
71
+ return last.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
72
+ }
73
+ }
74
+ catch { }
75
+ // Fallback: strip protocol and trailing slash
76
+ return name.replace(/^https?:\/\//, "").replace(/\/$/, "");
77
+ }
78
+ return name;
79
+ }
57
80
  function formatMatch(m, rank) {
58
81
  const lines = [];
82
+ const name = cleanName(m.artifact.name, m.artifact.url);
59
83
  const verified = m.artifact.verified ? " ✓" : "";
60
84
  const pricing = m.artifact.pricing.free ? "Free" : m.artifact.pricing.model;
61
- lines.push(`### ${rank}. ${m.artifact.name}${verified}`);
85
+ lines.push(`### ${rank}. ${name}${verified}`);
62
86
  lines.push(`**Type:** ${m.artifact.type} | **Score:** ${m.compositeScore}/100 | **Rank:** ${m.artifact.unfragileRank}/100 | **Pricing:** ${pricing}`);
63
87
  lines.push(`**URL:** ${m.artifact.url}`);
64
88
  if (m.artifact.description)
@@ -150,7 +174,8 @@ server.tool("get_artifact", "Get full details and capabilities for a specific AI
150
174
  return { content: [{ type: "text", text: `No artifact found matching "${name}".` }] };
151
175
  }
152
176
  const lines = [];
153
- lines.push(`# ${match.artifact.name}`);
177
+ const artName = cleanName(match.artifact.name, match.artifact.url);
178
+ lines.push(`# ${artName}`);
154
179
  lines.push(`**Type:** ${match.artifact.type} | **UnfragileRank:** ${match.artifact.unfragileRank}/100`);
155
180
  lines.push(`**URL:** ${match.artifact.url}`);
156
181
  lines.push(`**Verified:** ${match.artifact.verified ? "Yes ✓" : "No"}`);
@@ -200,10 +225,12 @@ server.tool("compare", "Compare two AI artifacts side-by-side. Shows capabilitie
200
225
  if (!matchA && !matchB) {
201
226
  return { content: [{ type: "text", text: `Neither "${artifact_a}" nor "${artifact_b}" found.` }] };
202
227
  }
228
+ const nameA = matchA ? cleanName(matchA.artifact.name, matchA.artifact.url) : artifact_a;
229
+ const nameB = matchB ? cleanName(matchB.artifact.name, matchB.artifact.url) : artifact_b;
203
230
  const lines = [];
204
- lines.push(`# Compare: ${matchA?.artifact.name || artifact_a} vs ${matchB?.artifact.name || artifact_b}\n`);
231
+ lines.push(`# Compare: ${nameA} vs ${nameB}\n`);
205
232
  const row = (label, a, b) => `| ${label} | ${a} | ${b} |`;
206
- lines.push(`| | ${matchA?.artifact.name || "Not found"} | ${matchB?.artifact.name || "Not found"} |`);
233
+ lines.push(`| | ${nameA} | ${nameB} |`);
207
234
  lines.push(`|---|---|---|`);
208
235
  if (matchA && matchB) {
209
236
  lines.push(row("Type", matchA.artifact.type, matchB.artifact.type));
@@ -212,11 +239,11 @@ server.tool("compare", "Compare two AI artifacts side-by-side. Shows capabilitie
212
239
  lines.push(row("Verified", matchA.artifact.verified ? "✓" : "No", matchB.artifact.verified ? "✓" : "No"));
213
240
  lines.push(row("Times Matched", String(matchA.matchGraph.timesMatched), String(matchB.matchGraph.timesMatched)));
214
241
  lines.push(row("Capabilities", String(matchA.capabilities.length), String(matchB.capabilities.length)));
215
- lines.push(`\n## ${matchA.artifact.name} Capabilities`);
242
+ lines.push(`\n## ${nameA} Capabilities`);
216
243
  for (const cap of matchA.capabilities) {
217
244
  lines.push(`- **${cap.name}**: ${cap.description.slice(0, 150)}`);
218
245
  }
219
- lines.push(`\n## ${matchB.artifact.name} Capabilities`);
246
+ lines.push(`\n## ${nameB} Capabilities`);
220
247
  for (const cap of matchB.capabilities) {
221
248
  lines.push(`- **${cap.name}**: ${cap.description.slice(0, 150)}`);
222
249
  }
@@ -275,9 +302,10 @@ server.tool("find_stack", "Assemble a complete AI harness stack for a use case.
275
302
  totalTools += matches.length;
276
303
  lines.push(`## ${layer}\n`);
277
304
  for (const m of matches) {
305
+ const mName = cleanName(m.artifact.name, m.artifact.url);
278
306
  const verified = m.artifact.verified ? " ✓" : "";
279
307
  const pricing = m.artifact.pricing.free ? "Free" : m.artifact.pricing.model;
280
- lines.push(`**${m.artifact.name}${verified}** — ${pricing} | Rank: ${m.artifact.unfragileRank}/100`);
308
+ lines.push(`**${mName}${verified}** — ${pricing} | Rank: ${m.artifact.unfragileRank}/100`);
281
309
  if (m.artifact.description)
282
310
  lines.push(`${m.artifact.description.slice(0, 200)}`);
283
311
  if (m.capabilities.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unfragile/mcp-server",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Unfragile MCP Server — query the match graph for AI from any agent. Find AI tools, assemble harness stacks, compare artifacts.",
5
5
  "keywords": [
6
6
  "mcp",