@thenextgennexus/social-content-mcp-server 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,73 @@
1
+ # Social & Content MCP Server
2
+
3
+ Access Hacker News, Dev.to, IMDB, podcasts, and Eventbrite data through the Model Context Protocol — powered by [nexgendata](https://apify.com/nexgendata) on Apify.
4
+
5
+ ## Quick Start
6
+
7
+ ### Using npx (recommended)
8
+
9
+ ```bash
10
+ npx @nexgendata/social-content-mcp-server
11
+ ```
12
+
13
+ ### Install globally
14
+
15
+ ```bash
16
+ npm install -g @nexgendata/social-content-mcp-server
17
+ ```
18
+
19
+ ## Configure with Claude Desktop
20
+
21
+ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
22
+
23
+ ```json
24
+ {
25
+ "mcpServers": {
26
+ "social-content-mcp-server": {
27
+ "command": "npx",
28
+ "args": [
29
+ "-y",
30
+ "@nexgendata/social-content-mcp-server"
31
+ ],
32
+ "env": {
33
+ "APIFY_TOKEN": "your-apify-token-optional"
34
+ }
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ ## Configure with Cline
41
+
42
+ Add the same configuration to your Cline MCP settings.
43
+
44
+ ## Available Tools
45
+
46
+ | Tool | Description |
47
+ |------|-------------|
48
+ | `get_hackernews_stories` | Get top/new/best stories from Hacker News |
49
+ | `get_devto_articles` | Search and retrieve articles from Dev.to |
50
+ | `search_imdb` | Search for movies and TV shows on IMDB |
51
+ | `search_podcasts` | Search podcast episodes across platforms |
52
+ | `search_eventbrite` | Search for events on Eventbrite |
53
+
54
+
55
+ ## Environment Variables
56
+
57
+ | Variable | Required | Description |
58
+ |----------|----------|-------------|
59
+ | `APIFY_TOKEN` | No | Your Apify API token for authenticated access. Without it, the server uses the public endpoint (rate-limited). |
60
+
61
+ ## How It Works
62
+
63
+ This MCP server acts as a local stdio bridge to the [nexgendata Apify MCP endpoint](https://nexgendata--social-content-mcp-server.apify.actor/mcp). When you call a tool, it forwards the request to Apify and returns the results.
64
+
65
+ ## Links
66
+
67
+ - [Apify Store](https://apify.com/nexgendata/social-content-mcp-server)
68
+ - [GitHub Repository](https://github.com/TheNextGenNexus/social-media-mcp-servers)
69
+ - [nexgendata Blog](https://thenextgennexus.com)
70
+
71
+ ## License
72
+
73
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,189 @@
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 APIFY_ENDPOINT = "https://nexgendata--social-content-mcp-server.apify.actor/mcp";
6
+ const APIFY_TOKEN = process.env.APIFY_TOKEN || "";
7
+ async function callApifyTool(toolName, args) {
8
+ const url = new URL(APIFY_ENDPOINT);
9
+ if (APIFY_TOKEN) {
10
+ url.searchParams.set("token", APIFY_TOKEN);
11
+ }
12
+ const body = {
13
+ jsonrpc: "2.0",
14
+ id: 1,
15
+ method: "tools/call",
16
+ params: {
17
+ name: toolName,
18
+ arguments: args
19
+ }
20
+ };
21
+ const response = await fetch(url.toString(), {
22
+ method: "POST",
23
+ headers: { "Content-Type": "application/json" },
24
+ body: JSON.stringify(body)
25
+ });
26
+ if (!response.ok) {
27
+ const errorText = await response.text();
28
+ throw new Error(`Apify API error (${response.status}): ${errorText}`);
29
+ }
30
+ const data = await response.json();
31
+ if (data.error) {
32
+ throw new Error(`Tool error: ${JSON.stringify(data.error)}`);
33
+ }
34
+ // Extract text content from MCP response
35
+ const result = data.result;
36
+ if (result?.content && Array.isArray(result.content)) {
37
+ return result.content
38
+ .filter((c) => c.type === "text")
39
+ .map((c) => c.text)
40
+ .join("\n");
41
+ }
42
+ return JSON.stringify(data.result, null, 2);
43
+ }
44
+ const server = new McpServer({
45
+ name: "social-content-mcp-server",
46
+ version: "1.0.0"
47
+ });
48
+ server.registerTool("get_hackernews_stories", {
49
+ title: "Get Hackernews Stories",
50
+ description: "Get top/new/best stories from Hacker News",
51
+ inputSchema: {
52
+ category: z.string().default('top').describe('Story category: top, new, best, ask, show'),
53
+ maxItems: z.number().default(10).describe('Maximum stories to return')
54
+ },
55
+ annotations: {
56
+ readOnlyHint: true,
57
+ destructiveHint: false,
58
+ openWorldHint: true
59
+ }
60
+ }, async (args) => {
61
+ try {
62
+ const result = await callApifyTool("get_hackernews_stories", args);
63
+ return {
64
+ content: [{ type: "text", text: result }]
65
+ };
66
+ }
67
+ catch (error) {
68
+ const message = error instanceof Error ? error.message : String(error);
69
+ return {
70
+ content: [{ type: "text", text: `Error: ${message}` }],
71
+ isError: true
72
+ };
73
+ }
74
+ });
75
+ server.registerTool("get_devto_articles", {
76
+ title: "Get Devto Articles",
77
+ description: "Search Dev.to articles",
78
+ inputSchema: {
79
+ query: z.string().describe('Search query'),
80
+ maxItems: z.number().default(10).describe('Maximum articles')
81
+ },
82
+ annotations: {
83
+ readOnlyHint: true,
84
+ destructiveHint: false,
85
+ openWorldHint: true
86
+ }
87
+ }, async (args) => {
88
+ try {
89
+ const result = await callApifyTool("get_devto_articles", args);
90
+ return {
91
+ content: [{ type: "text", text: result }]
92
+ };
93
+ }
94
+ catch (error) {
95
+ const message = error instanceof Error ? error.message : String(error);
96
+ return {
97
+ content: [{ type: "text", text: `Error: ${message}` }],
98
+ isError: true
99
+ };
100
+ }
101
+ });
102
+ server.registerTool("search_imdb", {
103
+ title: "Search Imdb",
104
+ description: "Search IMDB movies and TV shows",
105
+ inputSchema: {
106
+ query: z.string().describe('Movie or show title'),
107
+ maxItems: z.number().default(5).describe('Maximum results')
108
+ },
109
+ annotations: {
110
+ readOnlyHint: true,
111
+ destructiveHint: false,
112
+ openWorldHint: true
113
+ }
114
+ }, async (args) => {
115
+ try {
116
+ const result = await callApifyTool("search_imdb", args);
117
+ return {
118
+ content: [{ type: "text", text: result }]
119
+ };
120
+ }
121
+ catch (error) {
122
+ const message = error instanceof Error ? error.message : String(error);
123
+ return {
124
+ content: [{ type: "text", text: `Error: ${message}` }],
125
+ isError: true
126
+ };
127
+ }
128
+ });
129
+ server.registerTool("search_podcasts", {
130
+ title: "Search Podcasts",
131
+ description: "Search podcast episodes",
132
+ inputSchema: {
133
+ query: z.string().describe('Podcast search query'),
134
+ maxItems: z.number().default(10).describe('Maximum episodes')
135
+ },
136
+ annotations: {
137
+ readOnlyHint: true,
138
+ destructiveHint: false,
139
+ openWorldHint: true
140
+ }
141
+ }, async (args) => {
142
+ try {
143
+ const result = await callApifyTool("search_podcasts", args);
144
+ return {
145
+ content: [{ type: "text", text: result }]
146
+ };
147
+ }
148
+ catch (error) {
149
+ const message = error instanceof Error ? error.message : String(error);
150
+ return {
151
+ content: [{ type: "text", text: `Error: ${message}` }],
152
+ isError: true
153
+ };
154
+ }
155
+ });
156
+ server.registerTool("search_eventbrite", {
157
+ title: "Search Eventbrite",
158
+ description: "Search Eventbrite events",
159
+ inputSchema: {
160
+ query: z.string().describe('Event search query'),
161
+ location: z.string().optional().describe('City or region'),
162
+ maxItems: z.number().default(10).describe('Maximum events')
163
+ },
164
+ annotations: {
165
+ readOnlyHint: true,
166
+ destructiveHint: false,
167
+ openWorldHint: true
168
+ }
169
+ }, async (args) => {
170
+ try {
171
+ const result = await callApifyTool("search_eventbrite", args);
172
+ return {
173
+ content: [{ type: "text", text: result }]
174
+ };
175
+ }
176
+ catch (error) {
177
+ const message = error instanceof Error ? error.message : String(error);
178
+ return {
179
+ content: [{ type: "text", text: `Error: ${message}` }],
180
+ isError: true
181
+ };
182
+ }
183
+ });
184
+ async function main() {
185
+ const transport = new StdioServerTransport();
186
+ await server.connect(transport);
187
+ console.error("Social & Content MCP Server running on stdio");
188
+ }
189
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@thenextgennexus/social-content-mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "Access Hacker News, Dev.to, IMDB, podcasts, and Eventbrite data through the Model Context Protocol",
5
+ "type": "module",
6
+ "bin": {
7
+ "social-content-mcp-server": "./dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "start": "node dist/index.js",
12
+ "dev": "tsx src/index.ts"
13
+ },
14
+ "dependencies": {
15
+ "@modelcontextprotocol/sdk": "^1.0.0",
16
+ "zod": "^3.22.0"
17
+ },
18
+ "devDependencies": {
19
+ "typescript": "^5.3.0",
20
+ "tsx": "^4.7.0",
21
+ "@types/node": "^20.11.0"
22
+ },
23
+ "engines": {
24
+ "node": ">=18.0.0"
25
+ },
26
+ "keywords": [
27
+ "mcp",
28
+ "model-context-protocol",
29
+ "ai",
30
+ "apify",
31
+ "nexgendata"
32
+ ],
33
+ "author": "nexgendata",
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/TheNextGenNexus/social-media-mcp-servers"
38
+ },
39
+ "mcpName": "io.github.thenextgennexus/social-content-mcp-server"
40
+ }
package/smithery.yaml ADDED
@@ -0,0 +1,13 @@
1
+ startCommand:
2
+ type: stdio
3
+ configSchema:
4
+ type: object
5
+ required:
6
+ - apifyToken
7
+ properties:
8
+ apifyToken:
9
+ type: string
10
+ description: Your Apify API token. Get one free at https://apify.com
11
+ commandFunction:
12
+ |-
13
+ (config) => ({{ command: 'npx', args: ['tsx', 'src/index.ts'], env: {{ APIFY_TOKEN: config.apifyToken }} }})
package/src/index.ts ADDED
@@ -0,0 +1,222 @@
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
+
6
+ const APIFY_ENDPOINT = "https://nexgendata--social-content-mcp-server.apify.actor/mcp";
7
+ const APIFY_TOKEN = process.env.APIFY_TOKEN || "";
8
+
9
+ async function callApifyTool(toolName: string, args: Record<string, unknown>): Promise<string> {
10
+ const url = new URL(APIFY_ENDPOINT);
11
+ if (APIFY_TOKEN) {
12
+ url.searchParams.set("token", APIFY_TOKEN);
13
+ }
14
+
15
+ const body = {
16
+ jsonrpc: "2.0",
17
+ id: 1,
18
+ method: "tools/call",
19
+ params: {
20
+ name: toolName,
21
+ arguments: args
22
+ }
23
+ };
24
+
25
+ const response = await fetch(url.toString(), {
26
+ method: "POST",
27
+ headers: { "Content-Type": "application/json" },
28
+ body: JSON.stringify(body)
29
+ });
30
+
31
+ if (!response.ok) {
32
+ const errorText = await response.text();
33
+ throw new Error(`Apify API error (${response.status}): ${errorText}`);
34
+ }
35
+
36
+ const data = await response.json();
37
+
38
+ if (data.error) {
39
+ throw new Error(`Tool error: ${JSON.stringify(data.error)}`);
40
+ }
41
+
42
+ // Extract text content from MCP response
43
+ const result = data.result;
44
+ if (result?.content && Array.isArray(result.content)) {
45
+ return result.content
46
+ .filter((c: { type: string }) => c.type === "text")
47
+ .map((c: { text: string }) => c.text)
48
+ .join("\n");
49
+ }
50
+
51
+ return JSON.stringify(data.result, null, 2);
52
+ }
53
+
54
+ const server = new McpServer({
55
+ name: "social-content-mcp-server",
56
+ version: "1.0.0"
57
+ });
58
+
59
+ server.registerTool(
60
+ "get_hackernews_stories",
61
+ {
62
+ title: "Get Hackernews Stories",
63
+ description: "Get top/new/best stories from Hacker News",
64
+ inputSchema: {
65
+ category: z.string().default('top').describe('Story category: top, new, best, ask, show'),
66
+ maxItems: z.number().default(10).describe('Maximum stories to return')
67
+ },
68
+ annotations: {
69
+ readOnlyHint: true,
70
+ destructiveHint: false,
71
+ openWorldHint: true
72
+ }
73
+ },
74
+ async (args) => {
75
+ try {
76
+ const result = await callApifyTool("get_hackernews_stories", args);
77
+ return {
78
+ content: [{ type: "text", text: result }]
79
+ };
80
+ } catch (error) {
81
+ const message = error instanceof Error ? error.message : String(error);
82
+ return {
83
+ content: [{ type: "text", text: `Error: ${message}` }],
84
+ isError: true
85
+ };
86
+ }
87
+ }
88
+ );
89
+
90
+ server.registerTool(
91
+ "get_devto_articles",
92
+ {
93
+ title: "Get Devto Articles",
94
+ description: "Search Dev.to articles",
95
+ inputSchema: {
96
+ query: z.string().describe('Search query'),
97
+ maxItems: z.number().default(10).describe('Maximum articles')
98
+ },
99
+ annotations: {
100
+ readOnlyHint: true,
101
+ destructiveHint: false,
102
+ openWorldHint: true
103
+ }
104
+ },
105
+ async (args) => {
106
+ try {
107
+ const result = await callApifyTool("get_devto_articles", args);
108
+ return {
109
+ content: [{ type: "text", text: result }]
110
+ };
111
+ } catch (error) {
112
+ const message = error instanceof Error ? error.message : String(error);
113
+ return {
114
+ content: [{ type: "text", text: `Error: ${message}` }],
115
+ isError: true
116
+ };
117
+ }
118
+ }
119
+ );
120
+
121
+ server.registerTool(
122
+ "search_imdb",
123
+ {
124
+ title: "Search Imdb",
125
+ description: "Search IMDB movies and TV shows",
126
+ inputSchema: {
127
+ query: z.string().describe('Movie or show title'),
128
+ maxItems: z.number().default(5).describe('Maximum results')
129
+ },
130
+ annotations: {
131
+ readOnlyHint: true,
132
+ destructiveHint: false,
133
+ openWorldHint: true
134
+ }
135
+ },
136
+ async (args) => {
137
+ try {
138
+ const result = await callApifyTool("search_imdb", args);
139
+ return {
140
+ content: [{ type: "text", text: result }]
141
+ };
142
+ } catch (error) {
143
+ const message = error instanceof Error ? error.message : String(error);
144
+ return {
145
+ content: [{ type: "text", text: `Error: ${message}` }],
146
+ isError: true
147
+ };
148
+ }
149
+ }
150
+ );
151
+
152
+ server.registerTool(
153
+ "search_podcasts",
154
+ {
155
+ title: "Search Podcasts",
156
+ description: "Search podcast episodes",
157
+ inputSchema: {
158
+ query: z.string().describe('Podcast search query'),
159
+ maxItems: z.number().default(10).describe('Maximum episodes')
160
+ },
161
+ annotations: {
162
+ readOnlyHint: true,
163
+ destructiveHint: false,
164
+ openWorldHint: true
165
+ }
166
+ },
167
+ async (args) => {
168
+ try {
169
+ const result = await callApifyTool("search_podcasts", args);
170
+ return {
171
+ content: [{ type: "text", text: result }]
172
+ };
173
+ } catch (error) {
174
+ const message = error instanceof Error ? error.message : String(error);
175
+ return {
176
+ content: [{ type: "text", text: `Error: ${message}` }],
177
+ isError: true
178
+ };
179
+ }
180
+ }
181
+ );
182
+
183
+ server.registerTool(
184
+ "search_eventbrite",
185
+ {
186
+ title: "Search Eventbrite",
187
+ description: "Search Eventbrite events",
188
+ inputSchema: {
189
+ query: z.string().describe('Event search query'),
190
+ location: z.string().optional().describe('City or region'),
191
+ maxItems: z.number().default(10).describe('Maximum events')
192
+ },
193
+ annotations: {
194
+ readOnlyHint: true,
195
+ destructiveHint: false,
196
+ openWorldHint: true
197
+ }
198
+ },
199
+ async (args) => {
200
+ try {
201
+ const result = await callApifyTool("search_eventbrite", args);
202
+ return {
203
+ content: [{ type: "text", text: result }]
204
+ };
205
+ } catch (error) {
206
+ const message = error instanceof Error ? error.message : String(error);
207
+ return {
208
+ content: [{ type: "text", text: `Error: ${message}` }],
209
+ isError: true
210
+ };
211
+ }
212
+ }
213
+ );
214
+
215
+
216
+ async function main() {
217
+ const transport = new StdioServerTransport();
218
+ await server.connect(transport);
219
+ console.error("Social & Content MCP Server running on stdio");
220
+ }
221
+
222
+ main().catch(console.error);
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "declaration": true
12
+ },
13
+ "include": [
14
+ "src/**/*"
15
+ ]
16
+ }