storkai 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 +90 -0
- package/bin/storkai.js +174 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Stork MCP — The MCP Server for MCP Servers
|
|
2
|
+
|
|
3
|
+
Search, discover, evaluate, and configure MCP servers from inside your IDE. Install once, find any MCP server instantly.
|
|
4
|
+
|
|
5
|
+
**[stork.ai/mcp](https://www.stork.ai/mcp)**
|
|
6
|
+
|
|
7
|
+
## Add to your MCP client in 30 seconds
|
|
8
|
+
|
|
9
|
+
### Cursor
|
|
10
|
+
|
|
11
|
+
Add to `~/.cursor/mcp.json`:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"mcpServers": {
|
|
16
|
+
"stork": {
|
|
17
|
+
"command": "npx",
|
|
18
|
+
"args": ["-y", "storkai"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Claude Desktop
|
|
25
|
+
|
|
26
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"mcpServers": {
|
|
31
|
+
"stork": {
|
|
32
|
+
"command": "npx",
|
|
33
|
+
"args": ["-y", "storkai"]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Claude Code
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
claude mcp add stork -- npx -y storkai
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### VS Code
|
|
46
|
+
|
|
47
|
+
Add to `~/.vscode/mcp.json`:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"mcpServers": {
|
|
52
|
+
"stork": {
|
|
53
|
+
"command": "npx",
|
|
54
|
+
"args": ["-y", "storkai"]
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## What can you do with Stork?
|
|
61
|
+
|
|
62
|
+
Once installed, just ask your AI assistant:
|
|
63
|
+
|
|
64
|
+
- "Find me an MCP server that can manage Jira tickets"
|
|
65
|
+
- "Show me MCP servers for database queries"
|
|
66
|
+
- "Get me the install config for the Postgres MCP server for Cursor"
|
|
67
|
+
- "Compare the GitHub and GitLab MCP servers"
|
|
68
|
+
- "What MCP servers are available for cloud infrastructure?"
|
|
69
|
+
|
|
70
|
+
## Tools
|
|
71
|
+
|
|
72
|
+
| Tool | Description |
|
|
73
|
+
|------|-------------|
|
|
74
|
+
| `stork_search` | Find MCP servers by natural language description |
|
|
75
|
+
| `stork_server_details` | Get full details about a specific MCP server |
|
|
76
|
+
| `stork_get_install_config` | Get ready-to-paste config for your IDE |
|
|
77
|
+
| `stork_compare` | Compare 2-5 MCP servers side by side |
|
|
78
|
+
| `stork_list_filters` | Browse available categories and filters |
|
|
79
|
+
| `stork_submit` | Submit a new MCP server to the registry |
|
|
80
|
+
|
|
81
|
+
## Why Stork?
|
|
82
|
+
|
|
83
|
+
- **One install, all MCP servers**: Search thousands of MCP servers without leaving your IDE
|
|
84
|
+
- **Ready-to-paste configs**: Get correct JSON config for Cursor, Claude, VS Code, and more
|
|
85
|
+
- **Trust signals**: See maintenance status, GitHub stars, downloads, and security scores
|
|
86
|
+
- **Tool inventory**: Know exactly what tools each server exposes before installing
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
MIT
|
package/bin/storkai.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Stork MCP — The MCP server for MCP servers.
|
|
4
|
+
*
|
|
5
|
+
* Search, discover, evaluate, and configure MCP servers from inside your IDE.
|
|
6
|
+
* Install once: npx storkai
|
|
7
|
+
*
|
|
8
|
+
* https://www.stork.ai/mcp
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import process from "node:process";
|
|
12
|
+
import { ConvexHttpClient } from "convex/browser";
|
|
13
|
+
|
|
14
|
+
const SERVER_NAME = "storkai";
|
|
15
|
+
const SERVER_VERSION = "1.0.0";
|
|
16
|
+
const CONVEX_URL = "https://expert-egret-407.convex.cloud";
|
|
17
|
+
|
|
18
|
+
const client = new ConvexHttpClient(process.env.STORK_CONVEX_URL || CONVEX_URL);
|
|
19
|
+
|
|
20
|
+
let stdinBuffer = Buffer.alloc(0);
|
|
21
|
+
|
|
22
|
+
function writeMessage(obj) {
|
|
23
|
+
const json = JSON.stringify(obj);
|
|
24
|
+
process.stdout.write(`Content-Length: ${Buffer.byteLength(json, "utf8")}\r\n\r\n`);
|
|
25
|
+
process.stdout.write(json);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parseOneMessage(buffer) {
|
|
29
|
+
const headerEnd = buffer.indexOf("\r\n\r\n");
|
|
30
|
+
if (headerEnd === -1) return null;
|
|
31
|
+
const match = buffer.slice(0, headerEnd).toString("utf8").match(/Content-Length:\s*(\d+)/i);
|
|
32
|
+
if (!match) throw new Error("Missing Content-Length header");
|
|
33
|
+
const length = Number(match[1]);
|
|
34
|
+
const bodyStart = headerEnd + 4;
|
|
35
|
+
if (buffer.length < bodyStart + length) return null;
|
|
36
|
+
const body = buffer.slice(bodyStart, bodyStart + length).toString("utf8");
|
|
37
|
+
return { message: JSON.parse(body), rest: buffer.slice(bodyStart + length) };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function rpcError(id, code, message, data) {
|
|
41
|
+
return { jsonrpc: "2.0", id: id ?? null, error: { code, message, ...(data !== undefined ? { data } : {}) } };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function rpcResult(id, result) {
|
|
45
|
+
return { jsonrpc: "2.0", id, result };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function obj(v) {
|
|
49
|
+
return v && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function fmt(v) {
|
|
53
|
+
try { return JSON.stringify(v, null, 2); } catch { return String(v); }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function fmtSearch(data) {
|
|
57
|
+
if (!data?.results?.length) return "No MCP servers found. Try different keywords or use stork_list_filters to browse categories.";
|
|
58
|
+
const lines = [`Found ${data.results.length} MCP server(s):\n`];
|
|
59
|
+
for (const [i, r] of data.results.entries()) {
|
|
60
|
+
lines.push(`${i + 1}. **${r.name}** (${r.slug})`);
|
|
61
|
+
lines.push(` ${r.summary}`);
|
|
62
|
+
if (r.trustScore != null) lines.push(` Trust Score: ${r.trustScore}/100`);
|
|
63
|
+
if (r.tools.length) lines.push(` Tools: ${r.tools.slice(0, 5).map(t => t.name).join(", ")}${r.tools.length > 5 ? ` (+${r.tools.length - 5} more)` : ""}`);
|
|
64
|
+
if (r.transportTypes.length) lines.push(` Transport: ${r.transportTypes.join(", ")}`);
|
|
65
|
+
if (r.installCommand) lines.push(` Install: ${r.installCommand}`);
|
|
66
|
+
if (r.authSummary) lines.push(` Auth: ${r.authSummary}`);
|
|
67
|
+
if (r.npmPackage) lines.push(` npm: ${r.npmPackage}`);
|
|
68
|
+
lines.push(` Homepage: ${r.homepageUrl}`, "");
|
|
69
|
+
}
|
|
70
|
+
lines.push("Use stork_server_details for full info, or stork_get_install_config for ready-to-paste config.");
|
|
71
|
+
return lines.join("\n");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function fmtDetails(d) {
|
|
75
|
+
if (!d) return "Server not found.";
|
|
76
|
+
const l = [`# ${d.name}\n`, d.description || d.summary, ""];
|
|
77
|
+
if (d.trustScore != null) l.push(`**Trust Score**: ${d.trustScore}/100`);
|
|
78
|
+
l.push(`**Category**: ${d.primaryCategory}`);
|
|
79
|
+
if (d.transportTypes?.length) l.push(`**Transport**: ${d.transportTypes.join(", ")}`);
|
|
80
|
+
if (d.license) l.push(`**License**: ${d.license}`);
|
|
81
|
+
if (d.npmPackage) l.push(`**npm**: ${d.npmPackage}`);
|
|
82
|
+
if (d.installCommand) l.push(`**Install**: \`${d.installCommand}\``);
|
|
83
|
+
if (d.authSummary) l.push(`**Auth**: ${d.authSummary}`);
|
|
84
|
+
l.push(`**Homepage**: ${d.homepageUrl}`);
|
|
85
|
+
if (d.repoUrl) l.push(`**Repository**: ${d.repoUrl}`);
|
|
86
|
+
if (d.tools?.length) { l.push("\n## Tools"); for (const t of d.tools) l.push(`- **${t.name}**${t.description ? `: ${t.description}` : ""}`); }
|
|
87
|
+
l.push("\n---\nUse stork_get_install_config for ready-to-paste config.");
|
|
88
|
+
return l.join("\n");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function fmtConfig(d) {
|
|
92
|
+
if (d?.error) return `Error: ${d.error}`;
|
|
93
|
+
const l = [`# Install ${d.serverName} for ${d.client}\n`, d.instructions, "", "```json", d.configJson, "```"];
|
|
94
|
+
if (d.authSummary) l.push("", `**Auth required**: ${d.authSummary}`);
|
|
95
|
+
if (d.docsUrl) l.push(`**Documentation**: ${d.docsUrl}`);
|
|
96
|
+
return l.join("\n");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function fmtCompare(d) {
|
|
100
|
+
if (d?.error) return `Error: ${d.error}`;
|
|
101
|
+
if (!d?.comparison?.length) return "No servers to compare.";
|
|
102
|
+
const l = ["# MCP Server Comparison\n"];
|
|
103
|
+
for (const s of d.comparison) {
|
|
104
|
+
l.push(`## ${s.name} (${s.slug})`);
|
|
105
|
+
l.push(`- ${s.summary}`);
|
|
106
|
+
if (s.trustScore != null) l.push(`- Trust: ${s.trustScore}/100`);
|
|
107
|
+
l.push(`- Tools (${s.toolCount}): ${s.tools.slice(0, 8).join(", ") || "none"}`);
|
|
108
|
+
l.push(`- Transport: ${s.transportTypes.join(", ") || "stdio"}`);
|
|
109
|
+
l.push(`- License: ${s.license}`);
|
|
110
|
+
if (s.npmPackage) l.push(`- npm: ${s.npmPackage}`);
|
|
111
|
+
l.push(`- Homepage: ${s.homepageUrl}`, "");
|
|
112
|
+
}
|
|
113
|
+
return l.join("\n");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const tools = [
|
|
117
|
+
{ name: "stork_search", description: "Search for MCP servers by natural language. Example: 'manage Jira tickets' or 'query Postgres databases'. Returns top matches with trust scores, tools, and install commands.", inputSchema: { type: "object", required: ["query"], properties: { query: { type: "string", description: "What you need the MCP server to do" }, category: { type: "string", description: "Category filter" }, transport: { type: "string", enum: ["stdio", "sse", "streamable-http"], description: "Transport filter" }, limit: { type: "number", minimum: 1, maximum: 10, description: "Max results (default 3)" } } },
|
|
118
|
+
handler: async (a) => { const args = { query: String(a.query || ""), category: a.category || undefined, transport: a.transport || undefined, limit: typeof a.limit === "number" ? a.limit : undefined }; try { return fmtSearch(await client.action("mcpDiscoveryActions:semanticSearchServers", args)); } catch { return fmtSearch(await client.query("mcpDiscovery:searchServers", args)); } } },
|
|
119
|
+
{ name: "stork_server_details", description: "Get full details about an MCP server: tools, auth, trust scores, health, and more. Use the slug from search results.", inputSchema: { type: "object", required: ["slug"], properties: { slug: { type: "string", description: "Server slug" } } },
|
|
120
|
+
handler: async (a) => fmtDetails(await client.query("mcpDiscovery:getServerDetails", { slug: String(a.slug) })) },
|
|
121
|
+
{ name: "stork_get_install_config", description: "Get ready-to-paste JSON config for installing an MCP server in your IDE. Supports cursor, claude-desktop, claude-code, vscode, and zed.", inputSchema: { type: "object", required: ["slug", "client"], properties: { slug: { type: "string", description: "Server slug" }, client: { type: "string", enum: ["cursor", "claude-desktop", "claude-code", "vscode", "zed"], description: "Target IDE" } } },
|
|
122
|
+
handler: async (a) => fmtConfig(await client.query("mcpDiscovery:getInstallConfig", { slug: String(a.slug), client: String(a.client) })) },
|
|
123
|
+
{ name: "stork_compare", description: "Compare 2-5 MCP servers side by side on tools, trust, transport, auth, and more.", inputSchema: { type: "object", required: ["slugs"], properties: { slugs: { type: "array", items: { type: "string" }, minItems: 2, maxItems: 5, description: "Server slugs to compare" } } },
|
|
124
|
+
handler: async (a) => fmtCompare(await client.query("mcpDiscovery:compareServers", { slugs: Array.isArray(a.slugs) ? a.slugs.map(String) : [] })) },
|
|
125
|
+
{ name: "stork_list_filters", description: "List available categories and hosting filters with server counts.", inputSchema: { type: "object", properties: {} },
|
|
126
|
+
handler: async () => { const r = await client.query("mcpServers:listServerFilters", {}); const l = ["# Available Filters\n"]; if (r?.categories?.length) { l.push("## Categories"); for (const c of r.categories) l.push(`- ${c.id} (${c.count})`); } if (r?.hosting?.length) { l.push("\n## Hosting"); for (const h of r.hosting) l.push(`- ${h.id} (${h.count})`); } return l.join("\n"); } },
|
|
127
|
+
{ name: "stork_submit", description: "Submit a new MCP server to Stork. Provide a GitHub repo URL or npm package name.", inputSchema: { type: "object", required: ["url"], properties: { url: { type: "string", description: "GitHub repo URL or npm package" }, name: { type: "string", description: "Display name" }, description: { type: "string", description: "What the server does" } } },
|
|
128
|
+
handler: async (a) => `Submission received for: ${a.url}\n\nTo complete registration, visit: https://www.stork.ai/mcp/submit\nYour server will be auto-enriched with metadata, tools, and trust signals.` },
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
const toolMap = new Map(tools.map(t => [t.name, t]));
|
|
132
|
+
|
|
133
|
+
async function handle(msg) {
|
|
134
|
+
const { id, method, params } = msg;
|
|
135
|
+
if (method === "initialize") return rpcResult(id, { protocolVersion: "2024-11-05", capabilities: { tools: {} }, serverInfo: { name: SERVER_NAME, version: SERVER_VERSION } });
|
|
136
|
+
if (method === "tools/list") return rpcResult(id, { tools: tools.map(t => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })) });
|
|
137
|
+
if (method === "tools/call") {
|
|
138
|
+
const p = obj(params);
|
|
139
|
+
const tool = toolMap.get(String(p.name ?? ""));
|
|
140
|
+
if (!tool) return rpcResult(id, { content: [{ type: "text", text: `Unknown tool: ${p.name}` }], isError: true });
|
|
141
|
+
try {
|
|
142
|
+
const result = await tool.handler(obj(p.arguments));
|
|
143
|
+
return rpcResult(id, { content: [{ type: "text", text: typeof result === "string" ? result : fmt(result) }] });
|
|
144
|
+
} catch (err) {
|
|
145
|
+
return rpcResult(id, { content: [{ type: "text", text: `Error: ${err?.message ?? String(err)}` }], isError: true });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (method === "notifications/initialized" || method === "initialized") return null;
|
|
149
|
+
return rpcError(id, -32601, `Method not found: ${method}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
process.stdin.on("data", async (chunk) => {
|
|
153
|
+
stdinBuffer = Buffer.concat([stdinBuffer, chunk]);
|
|
154
|
+
while (true) {
|
|
155
|
+
let parsed;
|
|
156
|
+
try { parsed = parseOneMessage(stdinBuffer); } catch (err) {
|
|
157
|
+
writeMessage(rpcError(null, -32700, "Parse error", err?.message));
|
|
158
|
+
stdinBuffer = Buffer.alloc(0);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (!parsed) return;
|
|
162
|
+
stdinBuffer = parsed.rest;
|
|
163
|
+
const msg = parsed.message;
|
|
164
|
+
if (!msg || typeof msg !== "object" || msg.jsonrpc !== "2.0" || !("method" in msg)) {
|
|
165
|
+
writeMessage(rpcError(msg?.id ?? null, -32600, "Invalid Request"));
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (!("id" in msg)) { try { await handle(msg); } catch {} continue; }
|
|
169
|
+
try { const r = await handle(msg); if (r) writeMessage(r); }
|
|
170
|
+
catch (err) { writeMessage(rpcError(msg.id ?? null, -32603, "Internal error", err?.message)); }
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
process.stdin.on("end", () => process.exit(0));
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "storkai",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "The MCP server for MCP servers. Search, discover, and configure MCP servers from inside your IDE.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"storkai": "bin/storkai.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"keywords": [
|
|
14
|
+
"mcp",
|
|
15
|
+
"model-context-protocol",
|
|
16
|
+
"mcp-server",
|
|
17
|
+
"ai-tools",
|
|
18
|
+
"stork",
|
|
19
|
+
"storkai",
|
|
20
|
+
"discovery",
|
|
21
|
+
"registry",
|
|
22
|
+
"cursor",
|
|
23
|
+
"claude",
|
|
24
|
+
"vscode"
|
|
25
|
+
],
|
|
26
|
+
"author": "Stork.AI",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"homepage": "https://www.stork.ai/mcp",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/stork-ai/storkai"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"convex": "^1.31.2"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|