mcp-server-trending 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,94 @@
1
+ # mcp-server-trending ๐Ÿ”ฅ
2
+
3
+ > ่ฎฉ AI Agent ๅฎžๆ—ถ่Žทๅ– GitHubใ€Hacker News ็ญ‰ๅนณๅฐ็š„็ƒญ้—จ่ถ‹ๅŠฟๆ•ฐๆฎ
4
+
5
+ ไธ€ไธช่ฝป้‡็บง็š„ MCP๏ผˆModel Context Protocol๏ผ‰ๆœๅŠกๅ™จ๏ผŒไธบ AI ๅŠฉๆ‰‹ๆไพ›ๅฎžๆ—ถ็š„ๆŠ€ๆœฏ่ถ‹ๅŠฟ่ต„่ฎฏใ€‚**่ฎฉ AI ไธๅ†ไธŽไบ’่”็ฝ‘่„ฑ่Š‚ใ€‚**
6
+
7
+ ## ๐Ÿš€ ๅฟซ้€Ÿๅผ€ๅง‹
8
+
9
+ ### ๅฎ‰่ฃ…
10
+
11
+ ```bash
12
+ npx mcp-server-trending
13
+ ```
14
+
15
+ ### ้…็ฝฎ๏ผˆๅœจ MCP ๅฎขๆˆท็ซฏไธญ๏ผ‰
16
+
17
+ **Claude Code / OpenCode / Cursor:**
18
+ ```json
19
+ {
20
+ "mcpServers": {
21
+ "trending": {
22
+ "command": "npx",
23
+ "args": ["mcp-server-trending"]
24
+ }
25
+ }
26
+ }
27
+ ```
28
+
29
+ **Hermes Agent:**
30
+ ```yaml
31
+ # config.yaml
32
+ mcpServers:
33
+ trending:
34
+ command: npx
35
+ args: [mcp-server-trending]
36
+ ```
37
+
38
+ ## ๐Ÿ›  ๅทฅๅ…ทๅˆ—่กจ
39
+
40
+ ### 1. `get_github_trending`
41
+
42
+ ่Žทๅ– GitHub ็ƒญ้—จไป“ๅบ“
43
+
44
+ | ๅ‚ๆ•ฐ | ็ฑปๅž‹ | ้ป˜่ฎคๅ€ผ | ่ฏดๆ˜Ž |
45
+ |------|------|--------|------|
46
+ | `language` | string | - | ็ญ›้€‰่ฏญ่จ€ (ๅฆ‚ `typescript`, `python`, `rust`) |
47
+ | `since` | enum | `daily` | ๆ—ถ้—ด่Œƒๅ›ด: `daily` / `weekly` / `monthly` |
48
+ | `limit` | number | `10` | ่ฟ”ๅ›žๆ•ฐ้‡ (1-25) |
49
+
50
+ ### 2. `get_hacker_news`
51
+
52
+ ่Žทๅ– Hacker News ็ƒญ้—จๆ•…ไบ‹
53
+
54
+ | ๅ‚ๆ•ฐ | ็ฑปๅž‹ | ้ป˜่ฎคๅ€ผ | ่ฏดๆ˜Ž |
55
+ |------|------|--------|------|
56
+ | `type` | enum | `top` | ็ฑปๅž‹: `top` / `new` / `show` / `ask` / `best` |
57
+ | `limit` | number | `10` | ่ฟ”ๅ›žๆ•ฐ้‡ (1-30) |
58
+
59
+ ### 3. `get_trending_summary`
60
+
61
+ ไธ€้”ฎ่Žทๅ–ๅคšๆบ็ƒญ้—จๆ‘˜่ฆ๏ผˆGitHub + HN ๅŒๆ—ถๆ‹‰ๅ–๏ผ‰
62
+
63
+ | ๅ‚ๆ•ฐ | ็ฑปๅž‹ | ้ป˜่ฎคๅ€ผ | ่ฏดๆ˜Ž |
64
+ |------|------|--------|------|
65
+ | `sources` | array | `all` | ๆ•ฐๆฎๆบ: `["github", "hackernews"]` |
66
+
67
+ ## ๐Ÿ“‹ ่พ“ๅ‡บ็คบไพ‹
68
+
69
+ ```
70
+ ## ๐Ÿ”ฅ GitHub ไปŠๆ—ฅ็ƒญ้—จไป“ๅบ“
71
+
72
+ 1. โญ 58288 | code-yeongyu/oh-my-openagent (TypeScript)
73
+ omo; the best agent harness - previously oh-my-opencode
74
+ https://github.com/code-yeongyu/oh-my-openagent
75
+
76
+ ## ๐Ÿ“ฐ Hacker News ็ƒญ้—จๆ•…ไบ‹
77
+
78
+ 1. ๐Ÿ”— I turned a $80 RK3562 Android tablet into a Debian Linux workstation
79
+ โญ 306 ๅˆ† | ๐Ÿ‘ค tech4bot | ๐Ÿ’ฌ 141 ่ฏ„่ฎบ
80
+ ```
81
+
82
+ ## ๐Ÿ“ฆ ๅ‘ๅธƒ
83
+
84
+ ```bash
85
+ npm publish --access public
86
+ ```
87
+
88
+ ## ๐Ÿ“„ ่ฎธๅฏ
89
+
90
+ MIT - ๅ…่ดนไฝฟ็”จ๏ผŒๆฌข่ฟŽ่ดก็Œฎ
91
+
92
+ ---
93
+
94
+ **ไธบไป€ไนˆ้œ€่ฆ่ฟ™ไธช๏ผŸ** AI ไปฃ็†้œ€่ฆๅฎžๆ—ถๆ•ฐๆฎๆฅๅšๅ‡บๆ›ดๅฅฝ็š„ๅ†ณ็ญ–ใ€‚่ถ‹ๅŠฟๆ•ฐๆฎๅธฎๅŠฉ AI ไบ†่งฃๅฝ“ๅ‰ๆŠ€ๆœฏ็”Ÿๆ€็š„ๆœ€ๆ–ฐๅŠจๅ‘ใ€‚
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * mcp-server-trending โ€” MCP server providing real-time trending data
4
+ * for AI agents. Fetches from GitHub Trending, Hacker News, and more.
5
+ *
6
+ * Tools:
7
+ * get_github_trending โ€” trending repos by language/date range
8
+ * get_hacker_news โ€” top/new/show HN stories
9
+ * get_trending_summary โ€” quick multi-source digest
10
+ */
11
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * mcp-server-trending โ€” MCP server providing real-time trending data
4
+ * for AI agents. Fetches from GitHub Trending, Hacker News, and more.
5
+ *
6
+ * Tools:
7
+ * get_github_trending โ€” trending repos by language/date range
8
+ * get_hacker_news โ€” top/new/show HN stories
9
+ * get_trending_summary โ€” quick multi-source digest
10
+ */
11
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
12
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
14
+ import { z } from "zod";
15
+ // โ”€โ”€โ”€ Schemas โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
16
+ const GitHubTrendingSchema = z.object({
17
+ language: z.string().optional().describe("Filter by programming language (e.g. 'typescript', 'python', 'rust')"),
18
+ since: z.enum(["daily", "weekly", "monthly"]).optional().default("daily").describe("Time range"),
19
+ limit: z.number().min(1).max(25).optional().default(10).describe("Max repos to return"),
20
+ });
21
+ const HackerNewsSchema = z.object({
22
+ type: z.enum(["top", "new", "show", "ask", "best"]).optional().default("top").describe("Story type"),
23
+ limit: z.number().min(1).max(30).optional().default(10).describe("Max stories to return"),
24
+ });
25
+ const TrendingSummarySchema = z.object({
26
+ sources: z.array(z.enum(["github", "hackernews"])).optional().describe("Sources to include (default: all)"),
27
+ });
28
+ // โ”€โ”€โ”€ Fetch Functions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
29
+ async function fetchGitHubTrending(language, since = "daily", limit = 10) {
30
+ const url = language
31
+ ? `https://github.com/trending/${encodeURIComponent(language.toLowerCase())}?since=${since}`
32
+ : `https://github.com/trending?since=${since}`;
33
+ const res = await fetch(url, {
34
+ headers: {
35
+ "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36",
36
+ "Accept": "text/html",
37
+ },
38
+ signal: AbortSignal.timeout(15_000),
39
+ });
40
+ const html = await res.text();
41
+ // Extract repo hrefs from the server-rendered HTML shell
42
+ const repoRegex = /href="\/([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)"/g;
43
+ const matches = new Set();
44
+ let match;
45
+ while ((match = repoRegex.exec(html)) !== null) {
46
+ const name = match[1];
47
+ // Filter out non-repo links
48
+ if (!name.includes("login") && !name.includes("trending") && !name.includes("developers") && !name.includes("sponsors")) {
49
+ matches.add(name);
50
+ }
51
+ }
52
+ const repos = Array.from(matches).slice(0, limit);
53
+ // Fetch star counts for each repo
54
+ const results = [];
55
+ for (const fullName of repos) {
56
+ try {
57
+ const apiRes = await fetch(`https://api.github.com/repos/${fullName}`, {
58
+ headers: {
59
+ "User-Agent": "mcp-server-trending/1.0",
60
+ "Accept": "application/vnd.github.v3+json",
61
+ },
62
+ signal: AbortSignal.timeout(10_000),
63
+ });
64
+ if (apiRes.ok) {
65
+ const data = await apiRes.json();
66
+ results.push({
67
+ name: data.full_name,
68
+ url: data.html_url,
69
+ stars: data.stargazers_count,
70
+ forks: data.forks_count,
71
+ language: data.language,
72
+ description: data.description || "ๆ— ๆ่ฟฐ",
73
+ topics: data.topics || [],
74
+ });
75
+ }
76
+ else {
77
+ results.push({ name: fullName, url: `https://github.com/${fullName}`, stars: "?", description: "่Žทๅ–ๅคฑ่ดฅ" });
78
+ }
79
+ }
80
+ catch {
81
+ results.push({ name: fullName, url: `https://github.com/${fullName}`, stars: "?", description: "่Žทๅ–ๅคฑ่ดฅ" });
82
+ }
83
+ }
84
+ results.sort((a, b) => (typeof b.stars === 'number' ? b.stars : 0) - (typeof a.stars === 'number' ? a.stars : 0));
85
+ return results;
86
+ }
87
+ async function fetchHackerNews(type = "top", limit = 10) {
88
+ const endpoint = type === "top" ? "topstories" :
89
+ type === "new" ? "newstories" :
90
+ type === "best" ? "beststories" :
91
+ type === "show" ? "showstories" :
92
+ "askstories";
93
+ const res = await fetch(`https://hacker-news.firebaseio.com/v0/${endpoint}.json`, {
94
+ signal: AbortSignal.timeout(10_000),
95
+ });
96
+ const ids = (await res.json());
97
+ const topIds = ids.slice(0, limit);
98
+ const stories = await Promise.all(topIds.map(async (id) => {
99
+ try {
100
+ const sRes = await fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`, {
101
+ signal: AbortSignal.timeout(10_000),
102
+ });
103
+ const data = (await sRes.json());
104
+ return {
105
+ id: data.id,
106
+ title: data.title || "(ๆ— ๆ ‡้ข˜)",
107
+ url: data.url || `https://news.ycombinator.com/item?id=${data.id}`,
108
+ points: data.score || 0,
109
+ author: data.by || "anonymous",
110
+ comments: data.descendants || 0,
111
+ type: data.type || "story",
112
+ };
113
+ }
114
+ catch {
115
+ return null;
116
+ }
117
+ }));
118
+ return stories.filter((s) => s !== null)
119
+ .sort((a, b) => (b.points || 0) - (a.points || 0));
120
+ }
121
+ // โ”€โ”€โ”€ Server Setup โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
122
+ const server = new Server({ name: "mcp-server-trending", version: "0.1.0" }, { capabilities: { tools: {} } });
123
+ // List Tools
124
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
125
+ tools: [
126
+ {
127
+ name: "get_github_trending",
128
+ description: "่Žทๅ– GitHub ไปŠๆ—ฅ/ๆœฌๅ‘จ/ๆœฌๆœˆ็ƒญ้—จไป“ๅบ“ใ€‚ๅฏๆŒ‰็ผ–็จ‹่ฏญ่จ€็ญ›้€‰ใ€‚่ฟ”ๅ›žไป“ๅบ“ๅใ€Starๆ•ฐใ€ๆ่ฟฐ็ญ‰ใ€‚",
129
+ inputSchema: {
130
+ type: "object",
131
+ properties: {
132
+ language: { type: "string", description: "็ผ–็จ‹่ฏญ่จ€็ญ›้€‰ (ๅฆ‚ 'typescript', 'python', 'rust')" },
133
+ since: { type: "string", enum: ["daily", "weekly", "monthly"], default: "daily", description: "ๆ—ถ้—ด่Œƒๅ›ด" },
134
+ limit: { type: "number", default: 10, description: "่ฟ”ๅ›žๆ•ฐ้‡ (1-25)" },
135
+ },
136
+ },
137
+ },
138
+ {
139
+ name: "get_hacker_news",
140
+ description: "่Žทๅ– Hacker News ็ƒญ้—จ/ๆœ€ๆ–ฐๆ•…ไบ‹ใ€‚่ฟ”ๅ›žๆ ‡้ข˜ใ€้“พๆŽฅใ€ๅˆ†ๆ•ฐใ€่ฏ„่ฎบๆ•ฐ็ญ‰ใ€‚",
141
+ inputSchema: {
142
+ type: "object",
143
+ properties: {
144
+ type: { type: "string", enum: ["top", "new", "show", "ask", "best"], default: "top", description: "ๆ•…ไบ‹็ฑปๅž‹" },
145
+ limit: { type: "number", default: 10, description: "่ฟ”ๅ›žๆ•ฐ้‡ (1-30)" },
146
+ },
147
+ },
148
+ },
149
+ {
150
+ name: "get_trending_summary",
151
+ description: "ๅฟซ้€Ÿ่Žทๅ–ๅคšๆบ็ƒญ้—จๆ‘˜่ฆใ€‚ๅŒๆ—ถไปŽ GitHub ๅ’Œ Hacker News ๆ‹‰ๅ–ไปŠๆ—ฅ็ƒญ้—จ๏ผŒไธ€ๆฌก่ฟ”ๅ›žใ€‚",
152
+ inputSchema: {
153
+ type: "object",
154
+ properties: {
155
+ sources: { type: "array", items: { type: "string", enum: ["github", "hackernews"] }, description: "ๆ•ฐๆฎๆบ (้ป˜่ฎคๅ…จ้€‰)" },
156
+ },
157
+ },
158
+ },
159
+ ],
160
+ }));
161
+ // Call Tool
162
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
163
+ const { name, arguments: args } = request.params;
164
+ try {
165
+ switch (name) {
166
+ case "get_github_trending": {
167
+ const { language, since, limit } = GitHubTrendingSchema.parse(args || {});
168
+ const repos = await fetchGitHubTrending(language, since, limit);
169
+ const lines = repos.map((r, i) => `${i + 1}. โญ ${r.stars} | ${r.name}${r.language ? ` (${r.language})` : ""}\n ${r.description}\n ${r.url}`);
170
+ return {
171
+ content: [{
172
+ type: "text",
173
+ text: `## ๐Ÿ”ฅ GitHub ${since === "daily" ? "ไปŠๆ—ฅ" : since === "weekly" ? "ๆœฌๅ‘จ" : "ๆœฌๆœˆ"}็ƒญ้—จไป“ๅบ“\n${language ? `๏ผˆ่ฏญ่จ€: ${language}๏ผ‰` : ""}\n\n${lines.join("\n\n") || "ๆš‚ๆœช่Žทๅ–ๅˆฐๆ•ฐๆฎ"}`,
174
+ }],
175
+ };
176
+ }
177
+ case "get_hacker_news": {
178
+ const { type, limit } = HackerNewsSchema.parse(args || {});
179
+ const stories = await fetchHackerNews(type, limit);
180
+ const typeNames = {
181
+ top: "็ƒญ้—จ", new: "ๆœ€ๆ–ฐ", show: "Show HN", ask: "Ask HN", best: "ๆœ€ไฝณ",
182
+ };
183
+ const lines = stories.map((s, i) => `${i + 1}. ๐Ÿ”— ${s.title}\n โญ ${s.points} ๅˆ† | ๐Ÿ‘ค ${s.author} | ๐Ÿ’ฌ ${s.comments} ่ฏ„่ฎบ\n ${s.url}`);
184
+ return {
185
+ content: [{
186
+ type: "text",
187
+ text: `## ๐Ÿ“ฐ Hacker News ${typeNames[type] || type}ๆ•…ไบ‹\n\n${lines.join("\n\n") || "ๆš‚ๆœช่Žทๅ–ๅˆฐๆ•ฐๆฎ"}`,
188
+ }],
189
+ };
190
+ }
191
+ case "get_trending_summary": {
192
+ const { sources } = TrendingSummarySchema.parse(args || {});
193
+ const srcList = sources || ["github", "hackernews"];
194
+ const [ghRepos, hnStories] = await Promise.all([
195
+ srcList.includes("github") ? fetchGitHubTrending(undefined, "daily", 5) : Promise.resolve([]),
196
+ srcList.includes("hackernews") ? fetchHackerNews("top", 5) : Promise.resolve([]),
197
+ ]);
198
+ const parts = ["# ๐Ÿ“Š ไปŠๆ—ฅ็ƒญ้—จๆ‘˜่ฆ\n"];
199
+ if (ghRepos.length > 0) {
200
+ parts.push("## ๐Ÿ”ฅ GitHub ็ƒญ้—จ\n");
201
+ ghRepos.slice(0, 5).forEach((r, i) => {
202
+ parts.push(`${i + 1}. โญ ${r.stars} โ€” **${r.name}** โ€” ${String(r.description).slice(0, 80)}`);
203
+ });
204
+ parts.push("");
205
+ }
206
+ if (hnStories.length > 0) {
207
+ parts.push("## ๐Ÿ“ฐ Hacker News ็ƒญ้—จ\n");
208
+ hnStories.slice(0, 5).forEach((s, i) => {
209
+ parts.push(`${i + 1}. โญ ${s.points} โ€” **${s.title}**`);
210
+ });
211
+ parts.push("");
212
+ }
213
+ return { content: [{ type: "text", text: parts.join("\n") }] };
214
+ }
215
+ default:
216
+ throw new Error(`Unknown tool: ${name}`);
217
+ }
218
+ }
219
+ catch (error) {
220
+ const msg = error instanceof Error ? error.message : String(error);
221
+ return {
222
+ content: [{ type: "text", text: `้”™่ฏฏ: ${msg}` }],
223
+ isError: true,
224
+ };
225
+ }
226
+ });
227
+ // โ”€โ”€โ”€ Start โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
228
+ async function main() {
229
+ const transport = new StdioServerTransport();
230
+ await server.connect(transport);
231
+ console.error("[mcp-server-trending] ๆœๅŠกๅ™จๅทฒๅฏๅŠจ๏ผŒ็ญ‰ๅพ… MCP ๅฎขๆˆท็ซฏ่ฟžๆŽฅ...");
232
+ }
233
+ main().catch((err) => {
234
+ console.error("[mcp-server-trending] ๅฏๅŠจๅคฑ่ดฅ:", err);
235
+ process.exit(1);
236
+ });
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "mcp-server-trending",
3
+ "version": "0.1.0",
4
+ "description": "MCP server providing real-time trending data from GitHub, Hacker News, and more โ€” for AI agents",
5
+ "type": "module",
6
+ "bin": {
7
+ "mcp-server-trending": "dist/index.js"
8
+ },
9
+ "files": ["dist"],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node dist/index.js",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "keywords": ["mcp", "model-context-protocol", "trending", "github", "hackernews", "ai-agent"],
16
+ "license": "MIT",
17
+ "dependencies": {
18
+ "@modelcontextprotocol/sdk": "^1.19.0",
19
+ "zod": "^3.24.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^22.0.0",
23
+ "typescript": "^5.7.0"
24
+ }
25
+ }