@unfragile/mcp-server 0.2.0 → 0.3.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 +12 -3
  2. package/dist/index.js +154 -6
  3. package/package.json +5 -2
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @unfragile/mcp-server
2
2
 
3
- Query the [Unfragile match graph](https://unfragile.ai) from any AI agent. Find AI tools, assemble harness stacks, compare artifacts every query feeds the graph.
3
+ Query the [Unfragile match graph](https://unfragile.ai) from any AI agent. Find AI artifacts, discover MCP servers, assemble stacks, compare alternatives, and fetch trust passports. Every query feeds the graph.
4
+
5
+ Unfragile is built for the agent era: humans click links, agents call capabilities. This server gives Claude Code, Cursor, Windsurf, Claude Desktop, and custom MCP clients a runtime discovery and trust layer for AI artifacts.
4
6
 
5
7
  ## Install
6
8
 
@@ -62,24 +64,31 @@ Add to `~/.codeium/windsurf/mcp_config.json`:
62
64
  | `search` | Find AI tools by intent. "best framework for building AI agents" |
63
65
  | `find_mcps` | Discover MCP servers by capability. "Postgres + Slack integration" |
64
66
  | `get_artifact` | Get full details + capabilities for a specific artifact |
67
+ | `resolve_capability` | Resolve a `capability://...` URI to ranked trusted artifacts |
68
+ | `trust_passport` | Get machine-readable trust evidence: capability URIs, permissions, data access risk, failure modes |
65
69
  | `compare` | Compare two artifacts side-by-side |
66
70
  | `find_stack` | Assemble a complete harness stack for a use case |
71
+ | `feedback` | Report success/failure to improve future routing |
72
+ | `subscribe` | Watch for new artifacts matching a capability need |
73
+ | `unsubscribe` | Cancel a monitor |
67
74
 
68
75
  ## Examples
69
76
 
70
77
  Once installed, ask your agent:
71
78
 
72
79
  - "Find me MCP servers for Postgres and Slack"
80
+ - "Resolve capability://database.postgres.query.readonly"
73
81
  - "What's the best framework for building AI agents?"
74
82
  - "Compare LangChain vs CrewAI"
83
+ - "Get the trust passport for Cursor"
75
84
  - "I'm building a customer support agent — what tools do I need?"
76
85
  - "Get details on Cursor's capabilities"
77
86
 
78
87
  ## How it works
79
88
 
80
- The MCP server calls the [Unfragile API](https://unfragile.ai/api/v1/search) under the hood. Every query becomes a match record in the graph the graph learns from every interaction, regardless of where it happens.
89
+ The MCP server calls the [Unfragile API](https://unfragile.ai/docs) under the hood. Search and stack queries become match records in the graph. Trust passport requests use `/api/v1/passport/{slug}` to return artifact identity, capability URIs, constraints, permissions, data access risk, observed outcomes, and UnfragileRank evidence.
81
90
 
82
- 10,000+ AI artifacts. 33,000+ capabilities. The match graph for AI.
91
+ 13,160 active AI artifacts. 106,955 capabilities. 24 populated software categories. The match graph for AI.
83
92
 
84
93
  ## Environment Variables
85
94
 
package/dist/index.js CHANGED
@@ -10,6 +10,8 @@
10
10
  // search — Find AI tools by intent/query (with Match Proof)
11
11
  // find_mcps — Discover MCP servers by capability need
12
12
  // get_artifact — Get full details + capabilities for an artifact
13
+ // resolve_capability — Resolve capability:// URIs to trusted artifacts
14
+ // trust_passport — Get machine-readable trust passport for an artifact
13
15
  // compare — Compare two artifacts side-by-side
14
16
  // find_stack — Assemble a complete harness stack for a use case
15
17
  // feedback — Report success/failure to close the learning loop
@@ -55,6 +57,57 @@ async function searchAPI(query, options = {}) {
55
57
  clearTimeout(timeout);
56
58
  }
57
59
  }
60
+ async function passportAPI(slug) {
61
+ const headers = { Accept: "application/json" };
62
+ if (API_KEY)
63
+ headers["X-API-Key"] = API_KEY;
64
+ const controller = new AbortController();
65
+ const timeout = setTimeout(() => controller.abort(), 15_000);
66
+ try {
67
+ const res = await fetch(`${API_BASE}/api/v1/passport/${encodeURIComponent(slug)}`, {
68
+ headers,
69
+ signal: controller.signal,
70
+ });
71
+ if (!res.ok) {
72
+ const text = await res.text();
73
+ throw new Error(`Unfragile passport API error ${res.status}: ${text}`);
74
+ }
75
+ return res.json();
76
+ }
77
+ finally {
78
+ clearTimeout(timeout);
79
+ }
80
+ }
81
+ async function resolveAPI(capability, options = {}) {
82
+ const params = new URLSearchParams({ capability, source: SOURCE });
83
+ if (options.limit)
84
+ params.set("limit", String(options.limit));
85
+ if (options.type)
86
+ params.set("type", options.type);
87
+ if (options.risk)
88
+ params.set("risk", options.risk);
89
+ if (options.passport)
90
+ params.set("passport", "true");
91
+ const headers = { Accept: "application/json" };
92
+ if (API_KEY)
93
+ headers["X-API-Key"] = API_KEY;
94
+ const controller = new AbortController();
95
+ const timeout = setTimeout(() => controller.abort(), 15_000);
96
+ try {
97
+ const res = await fetch(`${API_BASE}/api/v1/resolve?${params}`, {
98
+ headers,
99
+ signal: controller.signal,
100
+ });
101
+ if (!res.ok) {
102
+ const text = await res.text();
103
+ throw new Error(`Unfragile resolver API error ${res.status}: ${text}`);
104
+ }
105
+ return res.json();
106
+ }
107
+ finally {
108
+ clearTimeout(timeout);
109
+ }
110
+ }
58
111
  // ─── Formatters ──────────────────────────────────────────────
59
112
  /** Extract a readable name from a URL when artifact.name is a raw URL */
60
113
  function cleanName(name, url) {
@@ -135,10 +188,76 @@ function formatResults(data) {
135
188
  }
136
189
  return lines.join("\n");
137
190
  }
191
+ function formatPassport(data) {
192
+ const p = data.unfragile;
193
+ const lines = [];
194
+ lines.push(`# Trust Passport: ${p.artifact.name}`);
195
+ lines.push(`**Type:** ${p.artifact.type} | **Trust Score:** ${p.trust.score}/100 | **Verified:** ${p.trust.verified ? "Yes ✓" : "No"}`);
196
+ lines.push(`**Data access risk:** ${p.trust.data_access_risk}`);
197
+ lines.push(`**URL:** ${p.artifact.url}`);
198
+ if (p.trust.permissions.length > 0) {
199
+ lines.push(`\n## Permissions / Requirements`);
200
+ for (const permission of p.trust.permissions)
201
+ lines.push(`- ${permission}`);
202
+ }
203
+ if (p.trust.failure_modes.length > 0) {
204
+ lines.push(`\n## Known Failure Modes`);
205
+ for (const failure of p.trust.failure_modes)
206
+ lines.push(`- ${failure}`);
207
+ }
208
+ lines.push(`\n## Observed Outcomes`);
209
+ lines.push(`- Matches: ${p.trust.observed_outcomes.matches}`);
210
+ lines.push(`- Success rate: ${Math.round(p.trust.observed_outcomes.success_rate * 100)}%`);
211
+ lines.push(`- Avg confidence: ${Math.round(p.trust.observed_outcomes.avg_confidence * 100)}%`);
212
+ if (p.trust.observed_outcomes.top_intents.length > 0) {
213
+ lines.push(`- Top intents: ${p.trust.observed_outcomes.top_intents.join(", ")}`);
214
+ }
215
+ if (p.capabilities.length > 0) {
216
+ lines.push(`\n## Capability URIs`);
217
+ for (const cap of p.capabilities.slice(0, 8)) {
218
+ lines.push(`- \`${cap.uri}\` — ${cap.name} (${Math.round(cap.confidence * 100)}% confidence)`);
219
+ }
220
+ }
221
+ lines.push(`\n→ Full passport JSON: ${data._links.self}`);
222
+ lines.push(`→ Artifact page: ${p.artifact.page_url}`);
223
+ return lines.join("\n");
224
+ }
225
+ function formatResolve(data) {
226
+ const lines = [];
227
+ lines.push(`# Resolve: ${data.capability}`);
228
+ lines.push(`Normalized query: ${data.normalizedQuery}`);
229
+ lines.push(`Mode: ${data.resolver.mode} | Risk mode: ${data.resolver.riskMode}`);
230
+ if (data.resolutions.length === 0) {
231
+ lines.push("\nNo trusted artifacts resolved for this capability. This is a demand gap.");
232
+ return lines.join("\n");
233
+ }
234
+ for (let i = 0; i < data.resolutions.length; i++) {
235
+ const r = data.resolutions[i];
236
+ const verified = r.artifact.verified ? " ✓" : "";
237
+ lines.push(`\n## ${i + 1}. ${r.artifact.name}${verified}`);
238
+ lines.push(`**Type:** ${r.artifact.type} | **Resolver score:** ${r.resolverScore}/100 | **Trust:** ${r.trust.score}/100 | **Risk:** ${r.dataAccessRisk}`);
239
+ lines.push(`**URL:** ${r.artifact.url}`);
240
+ if (r.capabilities.length > 0) {
241
+ lines.push("**Matched capabilities:**");
242
+ for (const cap of r.capabilities.slice(0, 4)) {
243
+ lines.push(`- ${cap.name} (${Math.round(cap.matchScore * 100)}% match)`);
244
+ if (cap.requires.length > 0)
245
+ lines.push(` Requires: ${cap.requires.join(", ")}`);
246
+ if (cap.limitations.length > 0)
247
+ lines.push(` Limitations: ${cap.limitations.join(", ")}`);
248
+ }
249
+ }
250
+ if (r.trust.observedMatches > 0) {
251
+ lines.push(`Observed outcomes: ${r.trust.observedMatches} matches, ${Math.round(r.trust.successRate * 100)}% success`);
252
+ }
253
+ lines.push(`Passport: ${r.artifact.passportUrl}`);
254
+ }
255
+ return lines.join("\n");
256
+ }
138
257
  // ─── MCP Server ──────────────────────────────────────────────
139
258
  const server = new McpServer({
140
259
  name: "unfragile",
141
- version: "0.2.0",
260
+ version: "0.3.0",
142
261
  });
143
262
  // Tool 1: General search
144
263
  server.tool("search", "Search the Unfragile match graph for AI tools, frameworks, APIs, MCP servers, agents, and more. Returns ranked results with capability matches and graph signals. Every query feeds the graph.", {
@@ -219,7 +338,36 @@ server.tool("get_artifact", "Get full details and capabilities for a specific AI
219
338
  return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
220
339
  }
221
340
  });
222
- // Tool 4: Compare artifacts
341
+ // Tool 4: Resolve capability
342
+ server.tool("resolve_capability", "Resolve a capability:// URI or natural-language capability into ranked AI artifacts. This is Unfragile's capability DNS primitive. Use when an agent knows what capability it needs and must choose the safest current artifact at runtime.", {
343
+ capability: z.string().min(2).max(500).describe("Capability URI or phrase, e.g. 'capability://database.postgres.query.readonly' or 'read-only Postgres database access'"),
344
+ limit: z.number().min(1).max(20).default(5).describe("Max resolutions"),
345
+ type: z.enum(["agent", "api", "app", "benchmark", "cli", "dataset", "extension", "finetune", "framework", "mcp", "model", "platform", "product", "prompt", "repo", "skill", "template", "webapp", "workflow"]).optional().describe("Filter by artifact type"),
346
+ risk: z.enum(["default", "production", "enterprise", "strict"]).default("default").describe("Risk posture. production/enterprise/strict require stronger quality/status gates."),
347
+ }, async ({ capability, limit, type, risk }) => {
348
+ log("resolve_capability", capability);
349
+ try {
350
+ const data = await resolveAPI(capability, { limit, type, risk });
351
+ return { content: [{ type: "text", text: formatResolve(data) }] };
352
+ }
353
+ catch (err) {
354
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
355
+ }
356
+ });
357
+ // Tool 5: Trust passport
358
+ server.tool("trust_passport", "Get the machine-readable trust passport for an AI artifact. Use this before an agent selects a tool, API, MCP server, model, repo, or framework for a task. Returns capability URIs, permissions, data access risk, known failure modes, observed outcomes, and trust score.", {
359
+ slug: z.string().min(1).max(200).describe("Artifact slug (e.g., 'cursor', 'claude-code', 'langchain')"),
360
+ }, async ({ slug }) => {
361
+ log("trust_passport", slug);
362
+ try {
363
+ const data = await passportAPI(slug);
364
+ return { content: [{ type: "text", text: formatPassport(data) }] };
365
+ }
366
+ catch (err) {
367
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
368
+ }
369
+ });
370
+ // Tool 6: Compare artifacts
223
371
  server.tool("compare", "Compare two AI artifacts side-by-side. Shows capabilities, pricing, rank, and graph signals for each. Uses search-based lookup (best-effort name matching). Use this when deciding between alternatives.", {
224
372
  artifact_a: z.string().min(1).max(200).describe("First artifact name (e.g., 'cursor')"),
225
373
  artifact_b: z.string().min(1).max(200).describe("Second artifact name (e.g., 'windsurf')"),
@@ -265,7 +413,7 @@ server.tool("compare", "Compare two AI artifacts side-by-side. Shows capabilitie
265
413
  return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
266
414
  }
267
415
  });
268
- // Tool 5: Find harness stack
416
+ // Tool 7: Find harness stack
269
417
  server.tool("find_stack", "Assemble a complete AI harness stack for a use case. Given a description of what you're building, returns recommended tools across harness layers: orchestration, tools/MCPs, memory, guardrails, context assembly, and evaluation. This is the key differentiator — Unfragile understands that modern AI systems are composed of 5-15 tools working together.", {
270
418
  description: z.string().min(10).max(1000).describe("What you're building (e.g., 'a customer support agent that connects to our Postgres database and Slack, with memory of past conversations')"),
271
419
  focus: z.enum(["full", "tools-only", "infrastructure"]).default("full").describe("Stack focus: 'full' = all layers, 'tools-only' = just MCPs and integrations, 'infrastructure' = frameworks and platforms"),
@@ -339,7 +487,7 @@ server.tool("find_stack", "Assemble a complete AI harness stack for a use case.
339
487
  return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
340
488
  }
341
489
  });
342
- // Tool 6: Feedback — close the learning loop
490
+ // Tool 8: Feedback — close the learning loop
343
491
  server.tool("feedback", "Report whether a recommended tool worked or not. This closes the learning loop — the Unfragile graph uses this feedback to improve future recommendations. Call this after trying a tool from search results.", {
344
492
  matchRecordId: z.string().min(1).describe("Match record ID from search results (shown at the bottom of search output)"),
345
493
  outcome: z.enum(["success", "failure"]).describe("Did the recommended tool work for your use case?"),
@@ -386,7 +534,7 @@ server.tool("feedback", "Report whether a recommended tool worked or not. This c
386
534
  return { content: [{ type: "text", text: `Error sending feedback: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
387
535
  }
388
536
  });
389
- // Tool 7: Subscribe — create a monitor
537
+ // Tool 9: Subscribe — create a monitor
390
538
  server.tool("subscribe", "Set up a persistent watch for new AI tools matching a query. Get notified daily when something new appears in the Unfragile graph. Requires at least one notification channel (email or webhook).", {
391
539
  query: z.string().min(2).max(500).describe("What to watch for (e.g., 'new MCP server for Postgres', 'framework for building AI agents')"),
392
540
  type: z.enum(["agent", "api", "app", "benchmark", "cli", "dataset", "extension", "finetune", "framework", "mcp", "model", "platform", "product", "prompt", "repo", "skill", "template", "webapp", "workflow"]).optional().describe("Only watch for this artifact type"),
@@ -450,7 +598,7 @@ server.tool("subscribe", "Set up a persistent watch for new AI tools matching a
450
598
  };
451
599
  }
452
600
  });
453
- // Tool 8: Unsubscribe — delete a monitor
601
+ // Tool 10: Unsubscribe — delete a monitor
454
602
  server.tool("unsubscribe", "Cancel a persistent watch (monitor). Use the monitor ID returned from subscribe.", {
455
603
  monitorId: z.string().min(1).describe("Monitor ID from subscribe (e.g., 'mon_...')"),
456
604
  }, async ({ monitorId }) => {
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@unfragile/mcp-server",
3
- "version": "0.2.0",
4
- "description": "Unfragile MCP Server — query the match graph for AI from any agent. Find AI tools, assemble harness stacks, compare artifacts.",
3
+ "version": "0.3.0",
4
+ "description": "Unfragile MCP Server — agent-native discovery, trust, and routing for AI artifacts.",
5
5
  "keywords": [
6
6
  "mcp",
7
7
  "ai",
8
8
  "tools",
9
9
  "discovery",
10
+ "trust",
11
+ "routing",
12
+ "capabilities",
10
13
  "unfragile",
11
14
  "harness-engineering"
12
15
  ],