vibeindex-mcp 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 ADDED
@@ -0,0 +1,127 @@
1
+ # Vibe Index MCP Server
2
+
3
+ Claude Code에서 [Vibe Index](https://vibeindex.ai)의 스킬, 플러그인, MCP 서버를 검색하고 설치할 수 있는 MCP 서버입니다.
4
+
5
+ ## 설치
6
+
7
+ ### Claude Desktop
8
+
9
+ `claude_desktop_config.json`에 추가:
10
+
11
+ ```json
12
+ {
13
+ "mcpServers": {
14
+ "vibeindex": {
15
+ "command": "npx",
16
+ "args": ["-y", "@vibeindex/mcp-server"]
17
+ }
18
+ }
19
+ }
20
+ ```
21
+
22
+ ### Claude Code
23
+
24
+ `.claude/settings.json`에 추가:
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "vibeindex": {
30
+ "command": "npx",
31
+ "args": ["-y", "@vibeindex/mcp-server"]
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ ## 사용 가능한 도구
38
+
39
+ ### search
40
+ 리소스 검색
41
+
42
+ ```
43
+ "git 관련 스킬 찾아줘"
44
+ "database MCP 서버 검색해줘"
45
+ ```
46
+
47
+ **파라미터:**
48
+ - `query` (필수): 검색어
49
+ - `type` (선택): all, skill, plugin, mcp, marketplace
50
+ - `limit` (선택): 결과 개수 (기본 10, 최대 20)
51
+
52
+ ### trending
53
+ 트렌딩 리소스 조회
54
+
55
+ ```
56
+ "요즘 인기있는 스킬 뭐야?"
57
+ "이번 주 트렌딩 보여줘"
58
+ ```
59
+
60
+ **파라미터:**
61
+ - `period` (선택): day, week, month (기본 week)
62
+ - `limit` (선택): 결과 개수 (기본 10)
63
+
64
+ ### top
65
+ 스타 순 상위 리소스
66
+
67
+ ```
68
+ "가장 인기있는 MCP 서버 알려줘"
69
+ "스타 많은 플러그인 top 5"
70
+ ```
71
+
72
+ **파라미터:**
73
+ - `type` (선택): all, skill, plugin, mcp, marketplace
74
+ - `limit` (선택): 결과 개수 (기본 10)
75
+
76
+ ### install
77
+ 설치 명령어 조회
78
+
79
+ ```
80
+ "commit-message 스킬 설치 방법 알려줘"
81
+ ```
82
+
83
+ **파라미터:**
84
+ - `name` (필수): 리소스 이름
85
+ - `type` (선택): 리소스 타입
86
+
87
+ ### categories
88
+ 카테고리 및 태그 목록
89
+
90
+ ```
91
+ "어떤 카테고리가 있어?"
92
+ ```
93
+
94
+ ## 예시
95
+
96
+ Claude Code에서:
97
+
98
+ ```
99
+ User: git 관련 스킬 찾아줘
100
+ Claude: [search 도구 사용] 검색 결과를 보여드립니다...
101
+
102
+ User: 그 중에 commit-message 설치해줘
103
+ Claude: [install 도구 사용] 설치 명령어입니다:
104
+ claude skill add owner/repo/commit-message
105
+ ```
106
+
107
+ ## 개발
108
+
109
+ ```bash
110
+ # 의존성 설치
111
+ npm install
112
+
113
+ # 빌드
114
+ npm run build
115
+
116
+ # 개발 모드 (watch)
117
+ npm run dev
118
+ ```
119
+
120
+ ## 라이선스
121
+
122
+ MIT
123
+
124
+ ## 링크
125
+
126
+ - [Vibe Index](https://vibeindex.ai)
127
+ - [GitHub](https://github.com/vibeindex/mcp-server)
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,300 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ const API_BASE = "https://vibeindex.ai/api";
6
+ // Helper to fetch from Vibe Index API
7
+ async function fetchAPI(endpoint) {
8
+ const response = await fetch(`${API_BASE}${endpoint}`);
9
+ if (!response.ok) {
10
+ throw new Error(`API error: ${response.status} ${response.statusText}`);
11
+ }
12
+ return response.json();
13
+ }
14
+ // Generate install command based on resource type
15
+ function getInstallCommand(resource) {
16
+ const { resource_type, github_owner, github_repo, name } = resource;
17
+ switch (resource_type) {
18
+ case "skill":
19
+ return `claude skill add ${github_owner}/${github_repo}/${name}`;
20
+ case "plugin":
21
+ return `claude plugin add ${github_owner}/${github_repo}`;
22
+ case "mcp":
23
+ return `# Add to claude_desktop_config.json or .claude/settings.json
24
+ # See: ${resource.github_url}`;
25
+ case "marketplace":
26
+ return `# Browse marketplace: https://vibeindex.ai/marketplaces/${github_owner}/${github_repo}`;
27
+ default:
28
+ return `# See: ${resource.github_url}`;
29
+ }
30
+ }
31
+ // Format resource for display
32
+ function formatResource(resource, includeInstall = false) {
33
+ const typeEmoji = {
34
+ skill: "🎯",
35
+ plugin: "🔌",
36
+ mcp: "🔗",
37
+ marketplace: "🏪",
38
+ };
39
+ const lines = [
40
+ `${typeEmoji[resource.resource_type] || "📦"} **${resource.name}**`,
41
+ ` Type: ${resource.resource_type}${resource.is_official ? " (Official)" : ""}`,
42
+ ` Stars: ⭐ ${resource.stars.toLocaleString()}`,
43
+ ` Repo: ${resource.github_owner}/${resource.github_repo}`,
44
+ ` Description: ${resource.description || "No description"}`,
45
+ ];
46
+ if (resource.tags?.length > 0) {
47
+ lines.push(` Tags: ${resource.tags.slice(0, 5).join(", ")}`);
48
+ }
49
+ if (includeInstall) {
50
+ lines.push(` Install: \`${getInstallCommand(resource)}\``);
51
+ }
52
+ lines.push(` URL: https://vibeindex.ai/${resource.resource_type}s/${resource.github_owner}/${resource.github_repo}/${resource.name}`);
53
+ return lines.join("\n");
54
+ }
55
+ // Create MCP server
56
+ const server = new McpServer({
57
+ name: "vibeindex",
58
+ version: "1.0.0",
59
+ });
60
+ // Tool: Search resources
61
+ server.tool("search", "Search for Claude Code skills, plugins, MCP servers, and marketplaces on Vibe Index", {
62
+ query: z.string().describe("Search query (e.g., 'git', 'database', 'docker')"),
63
+ type: z
64
+ .enum(["all", "skill", "plugin", "mcp", "marketplace"])
65
+ .optional()
66
+ .describe("Filter by resource type (default: all)"),
67
+ limit: z
68
+ .number()
69
+ .min(1)
70
+ .max(20)
71
+ .optional()
72
+ .describe("Number of results to return (default: 10, max: 20)"),
73
+ }, async ({ query, type = "all", limit = 10 }) => {
74
+ try {
75
+ const typeFilter = type !== "all" ? `&type=${type}` : "";
76
+ const data = await fetchAPI(`/resources?search=${encodeURIComponent(query)}${typeFilter}&pageSize=${limit}`);
77
+ if (data.data.length === 0) {
78
+ return {
79
+ content: [
80
+ {
81
+ type: "text",
82
+ text: `No results found for "${query}"${type !== "all" ? ` in ${type}s` : ""}. Try different keywords or browse at https://vibeindex.ai`,
83
+ },
84
+ ],
85
+ };
86
+ }
87
+ const results = data.data.map((r) => formatResource(r, true)).join("\n\n---\n\n");
88
+ return {
89
+ content: [
90
+ {
91
+ type: "text",
92
+ text: `## Search Results for "${query}" (${data.total} total)\n\n${results}\n\n---\nBrowse more at https://vibeindex.ai/search?q=${encodeURIComponent(query)}`,
93
+ },
94
+ ],
95
+ };
96
+ }
97
+ catch (error) {
98
+ return {
99
+ content: [
100
+ {
101
+ type: "text",
102
+ text: `Error searching: ${error instanceof Error ? error.message : "Unknown error"}`,
103
+ },
104
+ ],
105
+ isError: true,
106
+ };
107
+ }
108
+ });
109
+ // Tool: Get trending/rising resources
110
+ server.tool("trending", "Get trending and rising Claude Code resources from Vibe Index", {
111
+ period: z
112
+ .enum(["day", "week", "month"])
113
+ .optional()
114
+ .describe("Time period for trending (default: week)"),
115
+ limit: z
116
+ .number()
117
+ .min(1)
118
+ .max(20)
119
+ .optional()
120
+ .describe("Number of results (default: 10)"),
121
+ }, async ({ period = "week", limit = 10 }) => {
122
+ try {
123
+ const data = await fetchAPI(`/rising-stars?period=${period}&limit=${limit}`);
124
+ if (!data.rising || data.rising.length === 0) {
125
+ return {
126
+ content: [
127
+ {
128
+ type: "text",
129
+ text: `No trending resources found for the past ${period}. Check https://vibeindex.ai for the latest.`,
130
+ },
131
+ ],
132
+ };
133
+ }
134
+ const periodLabel = { day: "24 hours", week: "week", month: "month" }[period];
135
+ const results = data.rising
136
+ .map((r, i) => {
137
+ const base = formatResource(r, true);
138
+ return `**#${i + 1}** (+${r.stars_today} stars this ${period})\n${base}`;
139
+ })
140
+ .join("\n\n---\n\n");
141
+ return {
142
+ content: [
143
+ {
144
+ type: "text",
145
+ text: `## 🔥 Trending Resources (Past ${periodLabel})\n\n${results}\n\n---\nSee full rankings at https://vibeindex.ai`,
146
+ },
147
+ ],
148
+ };
149
+ }
150
+ catch (error) {
151
+ return {
152
+ content: [
153
+ {
154
+ type: "text",
155
+ text: `Error fetching trending: ${error instanceof Error ? error.message : "Unknown error"}`,
156
+ },
157
+ ],
158
+ isError: true,
159
+ };
160
+ }
161
+ });
162
+ // Tool: Get top resources by stars
163
+ server.tool("top", "Get top Claude Code resources by stars", {
164
+ type: z
165
+ .enum(["all", "skill", "plugin", "mcp", "marketplace"])
166
+ .optional()
167
+ .describe("Filter by resource type (default: all)"),
168
+ limit: z
169
+ .number()
170
+ .min(1)
171
+ .max(20)
172
+ .optional()
173
+ .describe("Number of results (default: 10)"),
174
+ }, async ({ type = "all", limit = 10 }) => {
175
+ try {
176
+ const typeFilter = type !== "all" ? `&type=${type}` : "";
177
+ const data = await fetchAPI(`/resources?sort=stars${typeFilter}&pageSize=${limit}`);
178
+ if (data.data.length === 0) {
179
+ return {
180
+ content: [
181
+ {
182
+ type: "text",
183
+ text: `No resources found. Check https://vibeindex.ai`,
184
+ },
185
+ ],
186
+ };
187
+ }
188
+ const typeLabel = type === "all" ? "Resources" : `${type}s`;
189
+ const results = data.data
190
+ .map((r, i) => `**#${i + 1}**\n${formatResource(r, true)}`)
191
+ .join("\n\n---\n\n");
192
+ return {
193
+ content: [
194
+ {
195
+ type: "text",
196
+ text: `## ⭐ Top ${typeLabel} by Stars\n\n${results}\n\n---\nBrowse all at https://vibeindex.ai/browse?sort=stars${type !== "all" ? `&type=${type}` : ""}`,
197
+ },
198
+ ],
199
+ };
200
+ }
201
+ catch (error) {
202
+ return {
203
+ content: [
204
+ {
205
+ type: "text",
206
+ text: `Error fetching top resources: ${error instanceof Error ? error.message : "Unknown error"}`,
207
+ },
208
+ ],
209
+ isError: true,
210
+ };
211
+ }
212
+ });
213
+ // Tool: Get install command
214
+ server.tool("install", "Get the install command for a Claude Code resource", {
215
+ name: z.string().describe("Resource name to get install command for"),
216
+ type: z
217
+ .enum(["skill", "plugin", "mcp", "marketplace"])
218
+ .optional()
219
+ .describe("Resource type (helps find exact match)"),
220
+ }, async ({ name, type }) => {
221
+ try {
222
+ const typeFilter = type ? `&type=${type}` : "";
223
+ const data = await fetchAPI(`/resources?search=${encodeURIComponent(name)}${typeFilter}&pageSize=5`);
224
+ // Find exact or close match
225
+ const exactMatch = data.data.find((r) => r.name.toLowerCase() === name.toLowerCase());
226
+ const resource = exactMatch || data.data[0];
227
+ if (!resource) {
228
+ return {
229
+ content: [
230
+ {
231
+ type: "text",
232
+ text: `Resource "${name}" not found. Try searching with: search "${name}"`,
233
+ },
234
+ ],
235
+ };
236
+ }
237
+ const installCmd = getInstallCommand(resource);
238
+ return {
239
+ content: [
240
+ {
241
+ type: "text",
242
+ text: `## Install ${resource.name}\n\n**Type:** ${resource.resource_type}\n**Repository:** ${resource.github_owner}/${resource.github_repo}\n\n**Install Command:**\n\`\`\`bash\n${installCmd}\n\`\`\`\n\n**More info:** https://vibeindex.ai/${resource.resource_type}s/${resource.github_owner}/${resource.github_repo}/${resource.name}`,
243
+ },
244
+ ],
245
+ };
246
+ }
247
+ catch (error) {
248
+ return {
249
+ content: [
250
+ {
251
+ type: "text",
252
+ text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`,
253
+ },
254
+ ],
255
+ isError: true,
256
+ };
257
+ }
258
+ });
259
+ // Tool: Browse categories
260
+ server.tool("categories", "List available categories and tags for Claude Code resources", {}, async () => {
261
+ const categories = {
262
+ "Resource Types": [
263
+ "🎯 Skills - Reusable agent capabilities",
264
+ "🔌 Plugins - Extend Claude Code functionality",
265
+ "🔗 MCP Servers - Model Context Protocol integrations",
266
+ "🏪 Marketplaces - Collections of plugins/skills",
267
+ ],
268
+ "Popular Tags": [
269
+ "git, github - Version control",
270
+ "database, sql, postgres - Data management",
271
+ "docker, kubernetes - Containers & orchestration",
272
+ "aws, gcp, azure - Cloud platforms",
273
+ "react, vue, nextjs - Frontend frameworks",
274
+ "python, typescript, rust - Programming languages",
275
+ "testing, lint, ci-cd - Development tools",
276
+ "ai, llm, openai - AI/ML tools",
277
+ ],
278
+ };
279
+ const text = Object.entries(categories)
280
+ .map(([cat, items]) => `### ${cat}\n${items.map((i) => `- ${i}`).join("\n")}`)
281
+ .join("\n\n");
282
+ return {
283
+ content: [
284
+ {
285
+ type: "text",
286
+ text: `## Vibe Index Categories\n\n${text}\n\n---\nUse \`search <keyword>\` to find resources in any category.\nBrowse all at https://vibeindex.ai/explore`,
287
+ },
288
+ ],
289
+ };
290
+ });
291
+ // Start server
292
+ async function main() {
293
+ const transport = new StdioServerTransport();
294
+ await server.connect(transport);
295
+ console.error("Vibe Index MCP Server running on stdio");
296
+ }
297
+ main().catch((error) => {
298
+ console.error("Fatal error:", error);
299
+ process.exit(1);
300
+ });
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "vibeindex-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Vibe Index - search Claude Code skills, plugins, and MCP servers",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "vibeindex-mcp": "./dist/index.js"
8
+ },
9
+ "type": "module",
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js"
14
+ },
15
+ "keywords": [
16
+ "mcp",
17
+ "claude",
18
+ "claude-code",
19
+ "vibeindex",
20
+ "skills",
21
+ "plugins"
22
+ ],
23
+ "author": "JoLab",
24
+ "license": "MIT",
25
+ "dependencies": {
26
+ "@modelcontextprotocol/sdk": "^1.0.0",
27
+ "zod": "^3.23.0"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^20.0.0",
31
+ "typescript": "^5.0.0"
32
+ }
33
+ }
package/src/index.ts ADDED
@@ -0,0 +1,392 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { z } from "zod";
6
+
7
+ const API_BASE = "https://vibeindex.ai/api";
8
+
9
+ interface Resource {
10
+ id: string;
11
+ name: string;
12
+ description: string;
13
+ description_ko?: string;
14
+ resource_type: "skill" | "plugin" | "mcp" | "marketplace";
15
+ github_owner: string;
16
+ github_repo: string;
17
+ github_url: string;
18
+ stars: number;
19
+ tags: string[];
20
+ is_official?: boolean;
21
+ install_command?: string;
22
+ }
23
+
24
+ interface SearchResponse {
25
+ data: Resource[];
26
+ total: number;
27
+ page: number;
28
+ limit: number;
29
+ }
30
+
31
+ interface TrendingResponse {
32
+ rising: Array<Resource & { stars_today: number; trending_rank: number }>;
33
+ period: string;
34
+ }
35
+
36
+ // Helper to fetch from Vibe Index API
37
+ async function fetchAPI<T>(endpoint: string): Promise<T> {
38
+ const response = await fetch(`${API_BASE}${endpoint}`);
39
+ if (!response.ok) {
40
+ throw new Error(`API error: ${response.status} ${response.statusText}`);
41
+ }
42
+ return response.json() as Promise<T>;
43
+ }
44
+
45
+ // Generate install command based on resource type
46
+ function getInstallCommand(resource: Resource): string {
47
+ const { resource_type, github_owner, github_repo, name } = resource;
48
+
49
+ switch (resource_type) {
50
+ case "skill":
51
+ return `claude skill add ${github_owner}/${github_repo}/${name}`;
52
+ case "plugin":
53
+ return `claude plugin add ${github_owner}/${github_repo}`;
54
+ case "mcp":
55
+ return `# Add to claude_desktop_config.json or .claude/settings.json
56
+ # See: ${resource.github_url}`;
57
+ case "marketplace":
58
+ return `# Browse marketplace: https://vibeindex.ai/marketplaces/${github_owner}/${github_repo}`;
59
+ default:
60
+ return `# See: ${resource.github_url}`;
61
+ }
62
+ }
63
+
64
+ // Format resource for display
65
+ function formatResource(resource: Resource, includeInstall = false): string {
66
+ const typeEmoji: Record<string, string> = {
67
+ skill: "🎯",
68
+ plugin: "🔌",
69
+ mcp: "🔗",
70
+ marketplace: "🏪",
71
+ };
72
+
73
+ const lines = [
74
+ `${typeEmoji[resource.resource_type] || "📦"} **${resource.name}**`,
75
+ ` Type: ${resource.resource_type}${resource.is_official ? " (Official)" : ""}`,
76
+ ` Stars: ⭐ ${resource.stars.toLocaleString()}`,
77
+ ` Repo: ${resource.github_owner}/${resource.github_repo}`,
78
+ ` Description: ${resource.description || "No description"}`,
79
+ ];
80
+
81
+ if (resource.tags?.length > 0) {
82
+ lines.push(` Tags: ${resource.tags.slice(0, 5).join(", ")}`);
83
+ }
84
+
85
+ if (includeInstall) {
86
+ lines.push(` Install: \`${getInstallCommand(resource)}\``);
87
+ }
88
+
89
+ lines.push(` URL: https://vibeindex.ai/${resource.resource_type}s/${resource.github_owner}/${resource.github_repo}/${resource.name}`);
90
+
91
+ return lines.join("\n");
92
+ }
93
+
94
+ // Create MCP server
95
+ const server = new McpServer({
96
+ name: "vibeindex",
97
+ version: "1.0.0",
98
+ });
99
+
100
+ // Tool: Search resources
101
+ server.tool(
102
+ "search",
103
+ "Search for Claude Code skills, plugins, MCP servers, and marketplaces on Vibe Index",
104
+ {
105
+ query: z.string().describe("Search query (e.g., 'git', 'database', 'docker')"),
106
+ type: z
107
+ .enum(["all", "skill", "plugin", "mcp", "marketplace"])
108
+ .optional()
109
+ .describe("Filter by resource type (default: all)"),
110
+ limit: z
111
+ .number()
112
+ .min(1)
113
+ .max(20)
114
+ .optional()
115
+ .describe("Number of results to return (default: 10, max: 20)"),
116
+ },
117
+ async ({ query, type = "all", limit = 10 }) => {
118
+ try {
119
+ const typeFilter = type !== "all" ? `&type=${type}` : "";
120
+ const data = await fetchAPI<SearchResponse>(
121
+ `/resources?search=${encodeURIComponent(query)}${typeFilter}&pageSize=${limit}`
122
+ );
123
+
124
+ if (data.data.length === 0) {
125
+ return {
126
+ content: [
127
+ {
128
+ type: "text" as const,
129
+ text: `No results found for "${query}"${type !== "all" ? ` in ${type}s` : ""}. Try different keywords or browse at https://vibeindex.ai`,
130
+ },
131
+ ],
132
+ };
133
+ }
134
+
135
+ const results = data.data.map((r) => formatResource(r, true)).join("\n\n---\n\n");
136
+
137
+ return {
138
+ content: [
139
+ {
140
+ type: "text" as const,
141
+ text: `## Search Results for "${query}" (${data.total} total)\n\n${results}\n\n---\nBrowse more at https://vibeindex.ai/search?q=${encodeURIComponent(query)}`,
142
+ },
143
+ ],
144
+ };
145
+ } catch (error) {
146
+ return {
147
+ content: [
148
+ {
149
+ type: "text" as const,
150
+ text: `Error searching: ${error instanceof Error ? error.message : "Unknown error"}`,
151
+ },
152
+ ],
153
+ isError: true,
154
+ };
155
+ }
156
+ }
157
+ );
158
+
159
+ // Tool: Get trending/rising resources
160
+ server.tool(
161
+ "trending",
162
+ "Get trending and rising Claude Code resources from Vibe Index",
163
+ {
164
+ period: z
165
+ .enum(["day", "week", "month"])
166
+ .optional()
167
+ .describe("Time period for trending (default: week)"),
168
+ limit: z
169
+ .number()
170
+ .min(1)
171
+ .max(20)
172
+ .optional()
173
+ .describe("Number of results (default: 10)"),
174
+ },
175
+ async ({ period = "week", limit = 10 }) => {
176
+ try {
177
+ const data = await fetchAPI<TrendingResponse>(
178
+ `/rising-stars?period=${period}&limit=${limit}`
179
+ );
180
+
181
+ if (!data.rising || data.rising.length === 0) {
182
+ return {
183
+ content: [
184
+ {
185
+ type: "text" as const,
186
+ text: `No trending resources found for the past ${period}. Check https://vibeindex.ai for the latest.`,
187
+ },
188
+ ],
189
+ };
190
+ }
191
+
192
+ const periodLabel = { day: "24 hours", week: "week", month: "month" }[period];
193
+ const results = data.rising
194
+ .map((r, i) => {
195
+ const base = formatResource(r, true);
196
+ return `**#${i + 1}** (+${r.stars_today} stars this ${period})\n${base}`;
197
+ })
198
+ .join("\n\n---\n\n");
199
+
200
+ return {
201
+ content: [
202
+ {
203
+ type: "text" as const,
204
+ text: `## 🔥 Trending Resources (Past ${periodLabel})\n\n${results}\n\n---\nSee full rankings at https://vibeindex.ai`,
205
+ },
206
+ ],
207
+ };
208
+ } catch (error) {
209
+ return {
210
+ content: [
211
+ {
212
+ type: "text" as const,
213
+ text: `Error fetching trending: ${error instanceof Error ? error.message : "Unknown error"}`,
214
+ },
215
+ ],
216
+ isError: true,
217
+ };
218
+ }
219
+ }
220
+ );
221
+
222
+ // Tool: Get top resources by stars
223
+ server.tool(
224
+ "top",
225
+ "Get top Claude Code resources by stars",
226
+ {
227
+ type: z
228
+ .enum(["all", "skill", "plugin", "mcp", "marketplace"])
229
+ .optional()
230
+ .describe("Filter by resource type (default: all)"),
231
+ limit: z
232
+ .number()
233
+ .min(1)
234
+ .max(20)
235
+ .optional()
236
+ .describe("Number of results (default: 10)"),
237
+ },
238
+ async ({ type = "all", limit = 10 }) => {
239
+ try {
240
+ const typeFilter = type !== "all" ? `&type=${type}` : "";
241
+ const data = await fetchAPI<SearchResponse>(
242
+ `/resources?sort=stars${typeFilter}&pageSize=${limit}`
243
+ );
244
+
245
+ if (data.data.length === 0) {
246
+ return {
247
+ content: [
248
+ {
249
+ type: "text" as const,
250
+ text: `No resources found. Check https://vibeindex.ai`,
251
+ },
252
+ ],
253
+ };
254
+ }
255
+
256
+ const typeLabel = type === "all" ? "Resources" : `${type}s`;
257
+ const results = data.data
258
+ .map((r, i) => `**#${i + 1}**\n${formatResource(r, true)}`)
259
+ .join("\n\n---\n\n");
260
+
261
+ return {
262
+ content: [
263
+ {
264
+ type: "text" as const,
265
+ text: `## ⭐ Top ${typeLabel} by Stars\n\n${results}\n\n---\nBrowse all at https://vibeindex.ai/browse?sort=stars${type !== "all" ? `&type=${type}` : ""}`,
266
+ },
267
+ ],
268
+ };
269
+ } catch (error) {
270
+ return {
271
+ content: [
272
+ {
273
+ type: "text" as const,
274
+ text: `Error fetching top resources: ${error instanceof Error ? error.message : "Unknown error"}`,
275
+ },
276
+ ],
277
+ isError: true,
278
+ };
279
+ }
280
+ }
281
+ );
282
+
283
+ // Tool: Get install command
284
+ server.tool(
285
+ "install",
286
+ "Get the install command for a Claude Code resource",
287
+ {
288
+ name: z.string().describe("Resource name to get install command for"),
289
+ type: z
290
+ .enum(["skill", "plugin", "mcp", "marketplace"])
291
+ .optional()
292
+ .describe("Resource type (helps find exact match)"),
293
+ },
294
+ async ({ name, type }) => {
295
+ try {
296
+ const typeFilter = type ? `&type=${type}` : "";
297
+ const data = await fetchAPI<SearchResponse>(
298
+ `/resources?search=${encodeURIComponent(name)}${typeFilter}&pageSize=5`
299
+ );
300
+
301
+ // Find exact or close match
302
+ const exactMatch = data.data.find(
303
+ (r) => r.name.toLowerCase() === name.toLowerCase()
304
+ );
305
+ const resource = exactMatch || data.data[0];
306
+
307
+ if (!resource) {
308
+ return {
309
+ content: [
310
+ {
311
+ type: "text" as const,
312
+ text: `Resource "${name}" not found. Try searching with: search "${name}"`,
313
+ },
314
+ ],
315
+ };
316
+ }
317
+
318
+ const installCmd = getInstallCommand(resource);
319
+
320
+ return {
321
+ content: [
322
+ {
323
+ type: "text" as const,
324
+ text: `## Install ${resource.name}\n\n**Type:** ${resource.resource_type}\n**Repository:** ${resource.github_owner}/${resource.github_repo}\n\n**Install Command:**\n\`\`\`bash\n${installCmd}\n\`\`\`\n\n**More info:** https://vibeindex.ai/${resource.resource_type}s/${resource.github_owner}/${resource.github_repo}/${resource.name}`,
325
+ },
326
+ ],
327
+ };
328
+ } catch (error) {
329
+ return {
330
+ content: [
331
+ {
332
+ type: "text" as const,
333
+ text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`,
334
+ },
335
+ ],
336
+ isError: true,
337
+ };
338
+ }
339
+ }
340
+ );
341
+
342
+ // Tool: Browse categories
343
+ server.tool(
344
+ "categories",
345
+ "List available categories and tags for Claude Code resources",
346
+ {},
347
+ async () => {
348
+ const categories = {
349
+ "Resource Types": [
350
+ "🎯 Skills - Reusable agent capabilities",
351
+ "🔌 Plugins - Extend Claude Code functionality",
352
+ "🔗 MCP Servers - Model Context Protocol integrations",
353
+ "🏪 Marketplaces - Collections of plugins/skills",
354
+ ],
355
+ "Popular Tags": [
356
+ "git, github - Version control",
357
+ "database, sql, postgres - Data management",
358
+ "docker, kubernetes - Containers & orchestration",
359
+ "aws, gcp, azure - Cloud platforms",
360
+ "react, vue, nextjs - Frontend frameworks",
361
+ "python, typescript, rust - Programming languages",
362
+ "testing, lint, ci-cd - Development tools",
363
+ "ai, llm, openai - AI/ML tools",
364
+ ],
365
+ };
366
+
367
+ const text = Object.entries(categories)
368
+ .map(([cat, items]) => `### ${cat}\n${items.map((i) => `- ${i}`).join("\n")}`)
369
+ .join("\n\n");
370
+
371
+ return {
372
+ content: [
373
+ {
374
+ type: "text" as const,
375
+ text: `## Vibe Index Categories\n\n${text}\n\n---\nUse \`search <keyword>\` to find resources in any category.\nBrowse all at https://vibeindex.ai/explore`,
376
+ },
377
+ ],
378
+ };
379
+ }
380
+ );
381
+
382
+ // Start server
383
+ async function main() {
384
+ const transport = new StdioServerTransport();
385
+ await server.connect(transport);
386
+ console.error("Vibe Index MCP Server running on stdio");
387
+ }
388
+
389
+ main().catch((error) => {
390
+ console.error("Fatal error:", error);
391
+ process.exit(1);
392
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "declaration": true
13
+ },
14
+ "include": ["src/**/*"],
15
+ "exclude": ["node_modules", "dist"]
16
+ }