@unfragile/mcp-server 0.1.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 ADDED
@@ -0,0 +1,93 @@
1
+ # @unfragile/mcp-server
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.
4
+
5
+ ## Install
6
+
7
+ ### Claude Code
8
+
9
+ ```bash
10
+ claude mcp add unfragile -- npx -y @unfragile/mcp-server
11
+ ```
12
+
13
+ ### Claude Desktop
14
+
15
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
16
+
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "unfragile": {
21
+ "command": "npx",
22
+ "args": ["-y", "@unfragile/mcp-server"]
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ ### Cursor
29
+
30
+ Add to `.cursor/mcp.json`:
31
+
32
+ ```json
33
+ {
34
+ "mcpServers": {
35
+ "unfragile": {
36
+ "command": "npx",
37
+ "args": ["-y", "@unfragile/mcp-server"]
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ ### Windsurf
44
+
45
+ Add to `~/.codeium/windsurf/mcp_config.json`:
46
+
47
+ ```json
48
+ {
49
+ "mcpServers": {
50
+ "unfragile": {
51
+ "command": "npx",
52
+ "args": ["-y", "@unfragile/mcp-server"]
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ ## Tools
59
+
60
+ | Tool | Description |
61
+ |------|-------------|
62
+ | `search` | Find AI tools by intent. "best framework for building AI agents" |
63
+ | `find_mcps` | Discover MCP servers by capability. "Postgres + Slack integration" |
64
+ | `get_artifact` | Get full details + capabilities for a specific artifact |
65
+ | `compare` | Compare two artifacts side-by-side |
66
+ | `find_stack` | Assemble a complete harness stack for a use case |
67
+
68
+ ## Examples
69
+
70
+ Once installed, ask your agent:
71
+
72
+ - "Find me MCP servers for Postgres and Slack"
73
+ - "What's the best framework for building AI agents?"
74
+ - "Compare LangChain vs CrewAI"
75
+ - "I'm building a customer support agent — what tools do I need?"
76
+ - "Get details on Cursor's capabilities"
77
+
78
+ ## How it works
79
+
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.
81
+
82
+ 10,000+ AI artifacts. 33,000+ capabilities. The match graph for AI.
83
+
84
+ ## Environment Variables
85
+
86
+ | Variable | Description | Default |
87
+ |----------|-------------|---------|
88
+ | `UNFRAGILE_API_URL` | API base URL | `https://unfragile.ai` |
89
+ | `UNFRAGILE_API_KEY` | API key for higher rate limits | (none) |
90
+
91
+ ## License
92
+
93
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,360 @@
1
+ #!/usr/bin/env node
2
+ // ─────────────────────────────────────────────────────────────
3
+ // Unfragile MCP Server
4
+ //
5
+ // Query the match graph for AI from any agent (Claude Code,
6
+ // Cursor, Windsurf, etc). Every query feeds the graph —
7
+ // the graph learns from every interaction.
8
+ //
9
+ // Tools:
10
+ // search — Find AI tools by intent/query
11
+ // find_mcps — Discover MCP servers by capability need
12
+ // get_artifact — Get full details + capabilities for an artifact
13
+ // compare — Compare two artifacts side-by-side
14
+ // find_stack — Assemble a complete harness stack for a use case
15
+ // feedback — Report success/failure to close the learning loop
16
+ // ─────────────────────────────────────────────────────────────
17
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
18
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
19
+ import { z } from "zod";
20
+ const API_BASE = process.env.UNFRAGILE_API_URL || "https://unfragile.ai";
21
+ const API_KEY = process.env.UNFRAGILE_API_KEY || "";
22
+ const SOURCE = "mcp-server";
23
+ function log(tool, query) {
24
+ console.error(`[unfragile] ${tool}: ${query}`);
25
+ }
26
+ async function searchAPI(query, options = {}) {
27
+ const params = new URLSearchParams({ q: query, source: SOURCE });
28
+ if (options.limit)
29
+ params.set("limit", String(options.limit));
30
+ if (options.type)
31
+ params.set("type", options.type);
32
+ const headers = { Accept: "application/json" };
33
+ if (API_KEY)
34
+ headers["X-API-Key"] = API_KEY;
35
+ const controller = new AbortController();
36
+ const timeout = setTimeout(() => controller.abort(), 10_000);
37
+ try {
38
+ const res = await fetch(`${API_BASE}/api/v1/search?${params}`, {
39
+ headers,
40
+ signal: controller.signal,
41
+ });
42
+ if (!res.ok) {
43
+ const text = await res.text();
44
+ throw new Error(`Unfragile API error ${res.status}: ${text}`);
45
+ }
46
+ const contentType = res.headers.get("content-type") || "";
47
+ if (!contentType.includes("application/json")) {
48
+ throw new Error(`Unfragile API returned ${contentType} instead of JSON. The API may be down or returning an error page.`);
49
+ }
50
+ return res.json();
51
+ }
52
+ finally {
53
+ clearTimeout(timeout);
54
+ }
55
+ }
56
+ // ─── Formatters ──────────────────────────────────────────────
57
+ function formatMatch(m, rank) {
58
+ const lines = [];
59
+ const verified = m.artifact.verified ? " ✓" : "";
60
+ const pricing = m.artifact.pricing.free ? "Free" : m.artifact.pricing.model;
61
+ lines.push(`### ${rank}. ${m.artifact.name}${verified}`);
62
+ lines.push(`**Type:** ${m.artifact.type} | **Score:** ${m.compositeScore}/100 | **Rank:** ${m.artifact.unfragileRank}/100 | **Pricing:** ${pricing}`);
63
+ lines.push(`**URL:** ${m.artifact.url}`);
64
+ if (m.artifact.description)
65
+ lines.push(`\n${m.artifact.description}`);
66
+ if (m.capabilities.length > 0) {
67
+ lines.push("\n**Matched Capabilities:**");
68
+ for (const cap of m.capabilities) {
69
+ lines.push(`- **${cap.name}** (${Math.round(cap.matchScore * 100)}% match)`);
70
+ if (cap.description)
71
+ lines.push(` ${cap.description.slice(0, 200)}`);
72
+ if (cap.bestFor.length > 0)
73
+ lines.push(` Best for: ${cap.bestFor.join(", ")}`);
74
+ if (cap.limitations.length > 0)
75
+ lines.push(` Limitations: ${cap.limitations.join(", ")}`);
76
+ }
77
+ }
78
+ if (m.matchGraph.timesMatched > 0) {
79
+ lines.push(`\n**Graph Signal:** Matched ${m.matchGraph.timesMatched} times | ${Math.round(m.matchGraph.successRate * 100)}% success`);
80
+ }
81
+ lines.push(`\n→ Details: ${m.artifact.pageUrl}`);
82
+ return lines.join("\n");
83
+ }
84
+ function formatResults(data) {
85
+ const lines = [];
86
+ lines.push(`# Search: "${data.query}"`);
87
+ lines.push(`Intent: ${data.intent.type} | Category: ${data.intent.category || "general"}`);
88
+ lines.push(`Found: ${data.matchCount} matches\n`);
89
+ if (data.matches.length === 0) {
90
+ lines.push("No matches found. This gap has been recorded — the Unfragile graph learns from every query.");
91
+ return lines.join("\n");
92
+ }
93
+ for (let i = 0; i < data.matches.length; i++) {
94
+ lines.push(formatMatch(data.matches[i], i + 1));
95
+ lines.push("");
96
+ }
97
+ // Include matchRecordIds for feedback
98
+ const ids = data.graphSignal.matchRecordIds;
99
+ if (ids && ids.length > 0) {
100
+ lines.push(`\n_Match record IDs (for feedback): ${ids.join(", ")}_`);
101
+ }
102
+ return lines.join("\n");
103
+ }
104
+ // ─── MCP Server ──────────────────────────────────────────────
105
+ const server = new McpServer({
106
+ name: "unfragile",
107
+ version: "0.1.0",
108
+ });
109
+ // Tool 1: General search
110
+ 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.", {
111
+ query: z.string().min(2).max(500).describe("What you're looking for (e.g., 'best framework for building AI agents', 'MCP server for database access')"),
112
+ limit: z.number().min(1).max(20).default(5).describe("Max results to return"),
113
+ 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"),
114
+ }, async ({ query, limit, type }) => {
115
+ log("search", query);
116
+ try {
117
+ const data = await searchAPI(query, { limit, type });
118
+ return { content: [{ type: "text", text: formatResults(data) }] };
119
+ }
120
+ catch (err) {
121
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
122
+ }
123
+ });
124
+ // Tool 2: Find MCP servers
125
+ server.tool("find_mcps", "Find MCP servers by capability need. Use this when you need to discover MCP servers for specific integrations (e.g., databases, APIs, cloud services). Returns MCP servers ranked by capability match.", {
126
+ need: z.string().min(2).max(500).describe("What capability you need (e.g., 'Postgres database access', 'Slack messaging', 'GitHub repository management')"),
127
+ limit: z.number().min(1).max(20).default(5).describe("Max results"),
128
+ }, async ({ need, limit }) => {
129
+ log("find_mcps", need);
130
+ try {
131
+ const data = await searchAPI(`MCP server for ${need}`, { limit, type: "mcp" });
132
+ const text = formatResults(data);
133
+ return { content: [{ type: "text", text }] };
134
+ }
135
+ catch (err) {
136
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
137
+ }
138
+ });
139
+ // Tool 3: Get artifact details
140
+ server.tool("get_artifact", "Get full details and capabilities for a specific AI artifact by name or slug. Uses search-based lookup (best-effort name matching — may return a different artifact for ambiguous names like 'express'). Use this to understand what an artifact can do before adding it to your stack.", {
141
+ name: z.string().min(1).max(200).describe("Artifact name or slug (e.g., 'cursor', 'langchain', 'claude-code')"),
142
+ }, async ({ name }) => {
143
+ log("get_artifact", name);
144
+ try {
145
+ const data = await searchAPI(name, { limit: 3 });
146
+ // Find best match by name
147
+ const match = data.matches.find((m) => m.artifact.name.toLowerCase() === name.toLowerCase() ||
148
+ m.artifact.slug === name.toLowerCase().replace(/\s+/g, "-")) || data.matches[0];
149
+ if (!match) {
150
+ return { content: [{ type: "text", text: `No artifact found matching "${name}".` }] };
151
+ }
152
+ const lines = [];
153
+ lines.push(`# ${match.artifact.name}`);
154
+ lines.push(`**Type:** ${match.artifact.type} | **UnfragileRank:** ${match.artifact.unfragileRank}/100`);
155
+ lines.push(`**URL:** ${match.artifact.url}`);
156
+ lines.push(`**Verified:** ${match.artifact.verified ? "Yes ✓" : "No"}`);
157
+ lines.push(`**Pricing:** ${match.artifact.pricing.free ? "Free" : match.artifact.pricing.model}`);
158
+ lines.push(`**Categories:** ${match.artifact.categories.join(", ")}`);
159
+ if (match.artifact.description)
160
+ lines.push(`\n${match.artifact.description}`);
161
+ if (match.capabilities.length > 0) {
162
+ lines.push("\n## Capabilities");
163
+ for (const cap of match.capabilities) {
164
+ lines.push(`\n### ${cap.name}`);
165
+ if (cap.description)
166
+ lines.push(cap.description);
167
+ if (cap.bestFor.length > 0)
168
+ lines.push(`\n**Best for:** ${cap.bestFor.join(", ")}`);
169
+ if (cap.limitations.length > 0)
170
+ lines.push(`**Limitations:** ${cap.limitations.join(", ")}`);
171
+ }
172
+ }
173
+ if (match.matchGraph.timesMatched > 0) {
174
+ lines.push(`\n## Graph Signal`);
175
+ lines.push(`Matched ${match.matchGraph.timesMatched} times | ${Math.round(match.matchGraph.successRate * 100)}% success rate`);
176
+ if (match.matchGraph.topIntents.length > 0) {
177
+ lines.push(`Top intents: ${match.matchGraph.topIntents.join(", ")}`);
178
+ }
179
+ }
180
+ lines.push(`\n→ Full details: ${match.artifact.pageUrl}`);
181
+ return { content: [{ type: "text", text: lines.join("\n") }] };
182
+ }
183
+ catch (err) {
184
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
185
+ }
186
+ });
187
+ // Tool 4: Compare artifacts
188
+ 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.", {
189
+ artifact_a: z.string().min(1).max(200).describe("First artifact name (e.g., 'cursor')"),
190
+ artifact_b: z.string().min(1).max(200).describe("Second artifact name (e.g., 'windsurf')"),
191
+ }, async ({ artifact_a, artifact_b }) => {
192
+ log("compare", `${artifact_a} vs ${artifact_b}`);
193
+ try {
194
+ const [dataA, dataB] = await Promise.all([
195
+ searchAPI(artifact_a, { limit: 1 }),
196
+ searchAPI(artifact_b, { limit: 1 }),
197
+ ]);
198
+ const matchA = dataA.matches[0];
199
+ const matchB = dataB.matches[0];
200
+ if (!matchA && !matchB) {
201
+ return { content: [{ type: "text", text: `Neither "${artifact_a}" nor "${artifact_b}" found.` }] };
202
+ }
203
+ const lines = [];
204
+ lines.push(`# Compare: ${matchA?.artifact.name || artifact_a} vs ${matchB?.artifact.name || artifact_b}\n`);
205
+ const row = (label, a, b) => `| ${label} | ${a} | ${b} |`;
206
+ lines.push(`| | ${matchA?.artifact.name || "Not found"} | ${matchB?.artifact.name || "Not found"} |`);
207
+ lines.push(`|---|---|---|`);
208
+ if (matchA && matchB) {
209
+ lines.push(row("Type", matchA.artifact.type, matchB.artifact.type));
210
+ lines.push(row("UnfragileRank", `${matchA.artifact.unfragileRank}/100`, `${matchB.artifact.unfragileRank}/100`));
211
+ lines.push(row("Pricing", matchA.artifact.pricing.free ? "Free" : matchA.artifact.pricing.model, matchB.artifact.pricing.free ? "Free" : matchB.artifact.pricing.model));
212
+ lines.push(row("Verified", matchA.artifact.verified ? "✓" : "No", matchB.artifact.verified ? "✓" : "No"));
213
+ lines.push(row("Times Matched", String(matchA.matchGraph.timesMatched), String(matchB.matchGraph.timesMatched)));
214
+ lines.push(row("Capabilities", String(matchA.capabilities.length), String(matchB.capabilities.length)));
215
+ lines.push(`\n## ${matchA.artifact.name} Capabilities`);
216
+ for (const cap of matchA.capabilities) {
217
+ lines.push(`- **${cap.name}**: ${cap.description.slice(0, 150)}`);
218
+ }
219
+ lines.push(`\n## ${matchB.artifact.name} Capabilities`);
220
+ for (const cap of matchB.capabilities) {
221
+ lines.push(`- **${cap.name}**: ${cap.description.slice(0, 150)}`);
222
+ }
223
+ lines.push(`\n→ Full comparison: https://unfragile.ai/compare/${matchA.artifact.slug}-vs-${matchB.artifact.slug}`);
224
+ }
225
+ return { content: [{ type: "text", text: lines.join("\n") }] };
226
+ }
227
+ catch (err) {
228
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
229
+ }
230
+ });
231
+ // Tool 5: Find harness stack
232
+ 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.", {
233
+ 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')"),
234
+ 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"),
235
+ }, async ({ description, focus }) => {
236
+ log("find_stack", description);
237
+ try {
238
+ // Parallel queries across harness layers
239
+ const layers = focus === "tools-only"
240
+ ? [
241
+ { name: "MCP Servers / Tools", query: `MCP server for ${description}`, type: "mcp" },
242
+ { name: "APIs", query: `API for ${description}`, type: "api" },
243
+ { name: "Extensions", query: `extension for ${description}`, type: "extension" },
244
+ ]
245
+ : focus === "infrastructure"
246
+ ? [
247
+ { name: "Frameworks", query: `framework for ${description}`, type: "framework" },
248
+ { name: "Platforms", query: `platform for ${description}`, type: "platform" },
249
+ { name: "CLI Tools", query: `CLI for ${description}`, type: "cli" },
250
+ ]
251
+ : [
252
+ { name: "Orchestration / Framework", query: `agent framework for ${description}`, type: "framework" },
253
+ { name: "MCP Servers / Tools", query: `MCP server for ${description}`, type: "mcp" },
254
+ { name: "APIs", query: `API for ${description}`, type: "api" },
255
+ { name: "Agents", query: `agent for ${description}`, type: "agent" },
256
+ { name: "CLI Tools", query: `CLI tool for ${description}`, type: "cli" },
257
+ ];
258
+ const results = await Promise.all(layers.map(async (layer) => {
259
+ try {
260
+ const data = await searchAPI(layer.query, { limit: 3, type: layer.type });
261
+ return { layer: layer.name, matches: data.matches };
262
+ }
263
+ catch {
264
+ return { layer: layer.name, matches: [] };
265
+ }
266
+ }));
267
+ const lines = [];
268
+ lines.push(`# Harness Stack for: ${description}\n`);
269
+ lines.push(`> Every AI system is a harness — the model is just one component.`);
270
+ lines.push(`> Here's a recommended stack assembled from the Unfragile match graph.\n`);
271
+ let totalTools = 0;
272
+ for (const { layer, matches } of results) {
273
+ if (matches.length === 0)
274
+ continue;
275
+ totalTools += matches.length;
276
+ lines.push(`## ${layer}\n`);
277
+ for (const m of matches) {
278
+ const verified = m.artifact.verified ? " ✓" : "";
279
+ const pricing = m.artifact.pricing.free ? "Free" : m.artifact.pricing.model;
280
+ lines.push(`**${m.artifact.name}${verified}** — ${pricing} | Rank: ${m.artifact.unfragileRank}/100`);
281
+ if (m.artifact.description)
282
+ lines.push(`${m.artifact.description.slice(0, 200)}`);
283
+ if (m.capabilities.length > 0) {
284
+ const capNames = m.capabilities.slice(0, 3).map((c) => c.name).join(", ");
285
+ lines.push(`Key capabilities: ${capNames}`);
286
+ }
287
+ lines.push(`→ ${m.artifact.url}\n`);
288
+ }
289
+ }
290
+ if (totalTools === 0) {
291
+ lines.push("No matching tools found for this use case. This gap has been recorded — the Unfragile graph learns from every query.");
292
+ }
293
+ else {
294
+ lines.push(`---`);
295
+ lines.push(`*${totalTools} tools across ${results.filter((r) => r.matches.length > 0).length} harness layers. Every query improves the graph.*`);
296
+ lines.push(`*Browse more: https://unfragile.ai/hub*`);
297
+ }
298
+ return { content: [{ type: "text", text: lines.join("\n") }] };
299
+ }
300
+ catch (err) {
301
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
302
+ }
303
+ });
304
+ // Tool 6: Feedback — close the learning loop
305
+ 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.", {
306
+ matchRecordId: z.string().min(1).describe("Match record ID from search results (shown at the bottom of search output)"),
307
+ outcome: z.enum(["success", "failure"]).describe("Did the recommended tool work for your use case?"),
308
+ comment: z.string().max(500).optional().describe("Optional: brief note on why it worked or didn't"),
309
+ }, async ({ matchRecordId, outcome, comment }) => {
310
+ log("feedback", `${matchRecordId} → ${outcome}`);
311
+ try {
312
+ const headers = { "Content-Type": "application/json" };
313
+ if (API_KEY)
314
+ headers["X-API-Key"] = API_KEY;
315
+ const body = {
316
+ matchRecordId,
317
+ outcome: outcome === "success" ? "success" : "failure",
318
+ clickedThrough: true,
319
+ source: SOURCE,
320
+ };
321
+ if (comment)
322
+ body.comment = comment;
323
+ const controller = new AbortController();
324
+ const timeout = setTimeout(() => controller.abort(), 10_000);
325
+ try {
326
+ const res = await fetch(`${API_BASE}/api/feedback`, {
327
+ method: "POST",
328
+ headers,
329
+ body: JSON.stringify(body),
330
+ signal: controller.signal,
331
+ });
332
+ if (!res.ok) {
333
+ const text = await res.text();
334
+ throw new Error(`Feedback API error ${res.status}: ${text}`);
335
+ }
336
+ }
337
+ finally {
338
+ clearTimeout(timeout);
339
+ }
340
+ return {
341
+ content: [{
342
+ type: "text",
343
+ text: `Feedback recorded: ${outcome}. The Unfragile graph will use this to improve future recommendations. Thank you!`,
344
+ }],
345
+ };
346
+ }
347
+ catch (err) {
348
+ return { content: [{ type: "text", text: `Error sending feedback: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
349
+ }
350
+ });
351
+ // ─── Start ───────────────────────────────────────────────────
352
+ async function main() {
353
+ const transport = new StdioServerTransport();
354
+ await server.connect(transport);
355
+ console.error("[unfragile] MCP server started");
356
+ }
357
+ main().catch((err) => {
358
+ console.error("Fatal:", err);
359
+ process.exit(1);
360
+ });
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@unfragile/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "Unfragile MCP Server — query the match graph for AI from any agent. Find AI tools, assemble harness stacks, compare artifacts.",
5
+ "keywords": [
6
+ "mcp",
7
+ "ai",
8
+ "tools",
9
+ "discovery",
10
+ "unfragile",
11
+ "harness-engineering"
12
+ ],
13
+ "license": "MIT",
14
+ "author": "Unfragile <hello@unfragile.ai>",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/Savirinc/Unfragile.ai",
18
+ "directory": "mcp-server"
19
+ },
20
+ "type": "module",
21
+ "main": "dist/index.js",
22
+ "bin": {
23
+ "unfragile-mcp": "dist/index.js"
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "scripts": {
29
+ "build": "tsc && chmod +x dist/index.js",
30
+ "dev": "tsx src/index.ts",
31
+ "start": "node dist/index.js",
32
+ "prepublishOnly": "npm run build"
33
+ },
34
+ "dependencies": {
35
+ "@modelcontextprotocol/sdk": "^1.29.0",
36
+ "zod": "^3.24.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^25.6.0",
40
+ "tsx": "^4.19.4",
41
+ "typescript": "^5.7.3"
42
+ },
43
+ "engines": {
44
+ "node": ">=18"
45
+ }
46
+ }