reddit-mcp-server 1.0.10 → 1.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 CHANGED
@@ -4,8 +4,6 @@ A Model Context Protocol (MCP) that provides tools for fetching and creating Red
4
4
 
5
5
  > **Note**: This is a fork of the original [reddit-mcp-server](https://github.com/alexandros-lekkas/reddit-mcp-server) by Alexandros Lekkas, updated with pnpm, tsup build system, and npx execution support.
6
6
 
7
- https://github.com/user-attachments/assets/caa37704-7c92-4bf8-b7e8-56d02ccb4983
8
-
9
7
  ## 🔧 Available Tools (Features)
10
8
 
11
9
  **Read-only Tools (Client Credentials):**
@@ -50,7 +48,7 @@ Make sure to select "script"!
50
48
 
51
49
  Do this with your `REDDIT_CLIENT_ID` and `REDDIT_CLIENT_SECRET`
52
50
 
53
- If you want to write posts you need to include your `REDDIT_USERNAME` and `REDDIT_PASSWORD` (don't worry, I won't steal them 😜)
51
+ If you want to write posts you need to include your `REDDIT_USERNAME` and `REDDIT_PASSWORD`
54
52
 
55
53
  5. Install dependencies with `pnpm install`
56
54
 
@@ -92,8 +90,6 @@ If you want to write posts you need to include your `REDDIT_USERNAME` and `REDDI
92
90
  }
93
91
  ```
94
92
 
95
- (Make sure to replace the environmental variables with your actual keys, not the 😜 emoji)
96
-
97
93
  ## 🛠️ Development
98
94
 
99
95
  ### Commands
@@ -129,6 +125,329 @@ npx reddit-mcp-server --version
129
125
 
130
126
  # Show help
131
127
  npx reddit-mcp-server --help
128
+
129
+ # Generate OAuth token for HTTP server
130
+ npx reddit-mcp-server --generate-token
131
+ ```
132
+
133
+ ### Streamable MCP Endpoint (Hono Server)
134
+
135
+ In addition to the standard npx execution, this server also supports a Streamable MCP endpoint via Hono for direct HTTP integration:
136
+
137
+ ```bash
138
+ # Start the Hono server on port 3000 (default)
139
+ pnpm serve
140
+
141
+ # Start with custom port
142
+ PORT=8080 pnpm serve
143
+
144
+ # Development mode with auto-reload
145
+ pnpm serve:dev
146
+ ```
147
+
148
+ The server will be available at `http://localhost:3000` with the MCP endpoint at `http://localhost:3000/mcp`.
149
+
150
+ #### OAuth Security (Optional)
151
+
152
+ The HTTP server supports optional OAuth protection to secure your endpoints:
153
+
154
+ **Generate a secure token:**
155
+ ```bash
156
+ npx reddit-mcp-server --generate-token
157
+ # Output: Generated OAuth token: A8f2Kp9x3NmQ7vR4tL6eZ1sW5yB8hC2j
158
+ ```
159
+
160
+ **Enable OAuth with generated token:**
161
+ ```bash
162
+ export OAUTH_ENABLED=true
163
+ export OAUTH_TOKEN="A8f2Kp9x3NmQ7vR4tL6eZ1sW5yB8hC2j"
164
+ pnpm serve
165
+ ```
166
+
167
+ **Make authenticated requests:**
168
+ ```bash
169
+ curl -H "Authorization: Bearer A8f2Kp9x3NmQ7vR4tL6eZ1sW5yB8hC2j" \
170
+ -H "Content-Type: application/json" \
171
+ -d '{"method":"tools/list","params":{}}' \
172
+ http://localhost:3000/mcp
173
+ ```
174
+
175
+ **OAuth Configuration:**
176
+ - `OAUTH_ENABLED=true` - Enables OAuth protection (disabled by default)
177
+ - `OAUTH_TOKEN=your-token` - Your custom token (or use `--generate-token`)
178
+ - Without OAuth, the server is accessible without authentication
179
+ - Health check (`/`) is always unprotected; only `/mcp` requires authentication
180
+
181
+ #### MCP Client Configuration
182
+
183
+ For MCP clients connecting to an OAuth-protected server, configure according to the [MCP Authorization specification](https://modelcontextprotocol.io/specification/draft/basic/authorization):
184
+
185
+ **HTTP-based MCP Clients (e.g., web applications):**
186
+ ```javascript
187
+ // Example using fetch API
188
+ const response = await fetch('http://localhost:3000/mcp', {
189
+ method: 'POST',
190
+ headers: {
191
+ 'Authorization': 'Bearer YOUR_TOKEN',
192
+ 'Content-Type': 'application/json'
193
+ },
194
+ body: JSON.stringify({
195
+ jsonrpc: "2.0",
196
+ id: 1,
197
+ method: "tools/list",
198
+ params: {}
199
+ })
200
+ });
201
+
202
+ const result = await response.json();
203
+ ```
204
+
205
+ **Direct HTTP MCP Client:**
206
+ ```javascript
207
+ const client = new MCP.Client({
208
+ transport: new MCP.HTTPTransport({
209
+ url: 'http://localhost:3000/mcp',
210
+ headers: {
211
+ 'Authorization': 'Bearer YOUR_TOKEN'
212
+ }
213
+ })
214
+ });
215
+ ```
216
+
217
+ **Custom MCP Client Implementation:**
218
+ ```typescript
219
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
220
+ import { HTTPTransport } from '@modelcontextprotocol/sdk/client/http.js';
221
+
222
+ const transport = new HTTPTransport({
223
+ url: 'http://localhost:3000/mcp',
224
+ headers: {
225
+ 'Authorization': 'Bearer YOUR_TOKEN',
226
+ 'Content-Type': 'application/json'
227
+ }
228
+ });
229
+
230
+ const client = new Client({
231
+ name: "reddit-client",
232
+ version: "1.0.0"
233
+ }, {
234
+ capabilities: {}
235
+ });
236
+
237
+ await client.connect(transport);
238
+ ```
239
+
240
+ **Important Notes:**
241
+ - Replace `YOUR_TOKEN` with your generated OAuth token
242
+ - Authorization header MUST be included in every request to `/mcp`
243
+ - Tokens MUST NOT be included in URI query strings per MCP specification
244
+ - Use HTTPS in production for secure token transmission
245
+
246
+ **For Remote/Deployed Servers:**
247
+ When connecting to a remote Reddit MCP server (e.g., deployed on your infrastructure):
248
+
249
+ ```javascript
250
+ // Production server with OAuth
251
+ const response = await fetch('https://your-server.com/mcp', {
252
+ method: 'POST',
253
+ headers: {
254
+ 'Authorization': 'Bearer YOUR_TOKEN',
255
+ 'Content-Type': 'application/json'
256
+ },
257
+ body: JSON.stringify({
258
+ jsonrpc: "2.0",
259
+ id: 1,
260
+ method: "tools/list",
261
+ params: {}
262
+ })
263
+ });
264
+ ```
265
+
266
+ **MCP Client Configuration for Remote Server:**
267
+ ```typescript
268
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
269
+ import { HTTPTransport } from '@modelcontextprotocol/sdk/client/http.js';
270
+
271
+ // For your deployed server
272
+ const transport = new HTTPTransport({
273
+ url: 'https://your-reddit-mcp.company.com/mcp',
274
+ headers: {
275
+ 'Authorization': 'Bearer YOUR_GENERATED_TOKEN',
276
+ 'Content-Type': 'application/json'
277
+ }
278
+ });
279
+
280
+ const client = new Client({
281
+ name: "reddit-client",
282
+ version: "1.0.0"
283
+ }, {
284
+ capabilities: {}
285
+ });
286
+
287
+ await client.connect(transport);
288
+ ```
289
+
290
+ **Claude Desktop/Cursor with Remote Server (HTTP):**
291
+ For remote servers, you can use a proxy approach:
292
+ ```json
293
+ {
294
+ "mcpServers": {
295
+ "reddit-remote": {
296
+ "command": "node",
297
+ "args": [
298
+ "-e",
299
+ "const http = require('https'); const req = http.request('https://your-server.com/mcp', {method:'POST',headers:{'Authorization':'Bearer YOUR_TOKEN','Content-Type':'application/json'}}, res => res.pipe(process.stdout)); process.stdin.pipe(req);"
300
+ ],
301
+ "env": {}
302
+ }
303
+ }
304
+ }
305
+ ```
306
+
307
+ **For Claude Desktop/Cursor (stdio transport):**
308
+ OAuth is not applicable when using the traditional npx execution method. Use the stdio configuration instead:
309
+ ```json
310
+ {
311
+ "mcpServers": {
312
+ "reddit": {
313
+ "command": "npx",
314
+ "args": ["reddit-mcp-server"],
315
+ "env": {
316
+ "REDDIT_CLIENT_ID": "your_client_id",
317
+ "REDDIT_CLIENT_SECRET": "your_client_secret"
318
+ }
319
+ }
320
+ }
321
+ }
322
+ ```
323
+
324
+ #### Remote Deployment Examples
325
+
326
+ **Deploy to your infrastructure and share the URL:**
327
+
328
+ 1. **Generate secure token:**
329
+ ```bash
330
+ npx reddit-mcp-server --generate-token
331
+ # Output: Generated OAuth token: xyz123abc456def789
332
+ ```
333
+
334
+ 2. **Deploy with Docker on your server:**
335
+ ```bash
336
+ docker run -d \
337
+ --name reddit-mcp \
338
+ -p 3000:3000 \
339
+ -e REDDIT_CLIENT_ID=your_reddit_client_id \
340
+ -e REDDIT_CLIENT_SECRET=your_reddit_client_secret \
341
+ -e OAUTH_ENABLED=true \
342
+ -e OAUTH_TOKEN=xyz123abc456def789 \
343
+ ghcr.io/jordanburke/reddit-mcp-server:latest
344
+ ```
345
+
346
+ 3. **Share with your team:**
347
+ ```
348
+ Server URL: https://your-server.com/mcp
349
+ OAuth Token: xyz123abc456def789
350
+ ```
351
+
352
+ 4. **Team members connect:**
353
+ ```typescript
354
+ const client = new Client(...);
355
+ const transport = new HTTPTransport({
356
+ url: 'https://your-server.com/mcp',
357
+ headers: { 'Authorization': 'Bearer xyz123abc456def789' }
358
+ });
359
+ await client.connect(transport);
360
+ ```
361
+
362
+ This allows integration with systems that support HTTP-based MCP communication, similar to the cq-api and agent-todo implementations.
363
+
364
+ ## 🐳 Docker Usage
365
+
366
+ ### Pull from GitHub Container Registry
367
+
368
+ ```bash
369
+ # Pull the latest image
370
+ docker pull ghcr.io/jordanburke/reddit-mcp-server:latest
371
+
372
+ # Pull a specific version
373
+ docker pull ghcr.io/jordanburke/reddit-mcp-server:v1.0.10
374
+ ```
375
+
376
+ ### Run with Docker
377
+
378
+ ```bash
379
+ # Run the HTTP server (recommended)
380
+ docker run -d \
381
+ --name reddit-mcp \
382
+ -p 3000:3000 \
383
+ -e REDDIT_CLIENT_ID=your_client_id \
384
+ -e REDDIT_CLIENT_SECRET=your_client_secret \
385
+ -e REDDIT_USERNAME=your_username \
386
+ -e REDDIT_PASSWORD=your_password \
387
+ ghcr.io/jordanburke/reddit-mcp-server:latest
388
+
389
+ # Run with OAuth enabled (secure)
390
+ docker run -d \
391
+ --name reddit-mcp \
392
+ -p 3000:3000 \
393
+ -e REDDIT_CLIENT_ID=your_client_id \
394
+ -e REDDIT_CLIENT_SECRET=your_client_secret \
395
+ -e OAUTH_ENABLED=true \
396
+ -e OAUTH_TOKEN=your_generated_token \
397
+ ghcr.io/jordanburke/reddit-mcp-server:latest
398
+
399
+ # Run with custom port
400
+ docker run -d \
401
+ --name reddit-mcp \
402
+ -p 8080:3000 \
403
+ --env-file .env \
404
+ ghcr.io/jordanburke/reddit-mcp-server:latest
405
+
406
+ # Generate token using Docker
407
+ docker run --rm \
408
+ ghcr.io/jordanburke/reddit-mcp-server:latest \
409
+ node dist/bin.js --generate-token
410
+
411
+ # Run as stdio MCP server (for direct integration)
412
+ docker run -it \
413
+ --env-file .env \
414
+ ghcr.io/jordanburke/reddit-mcp-server:latest \
415
+ node dist/index.js
416
+ ```
417
+
418
+ ### Build Locally
419
+
420
+ ```bash
421
+ # Build the image
422
+ docker build -t reddit-mcp-server .
423
+
424
+ # Run the locally built image
425
+ docker run -d \
426
+ --name reddit-mcp \
427
+ -p 3000:3000 \
428
+ --env-file .env \
429
+ reddit-mcp-server
430
+ ```
431
+
432
+ ### Docker Compose Example
433
+
434
+ ```yaml
435
+ version: '3.8'
436
+
437
+ services:
438
+ reddit-mcp:
439
+ image: ghcr.io/jordanburke/reddit-mcp-server:latest
440
+ ports:
441
+ - "3000:3000"
442
+ environment:
443
+ - REDDIT_CLIENT_ID=${REDDIT_CLIENT_ID}
444
+ - REDDIT_CLIENT_SECRET=${REDDIT_CLIENT_SECRET}
445
+ - REDDIT_USERNAME=${REDDIT_USERNAME}
446
+ - REDDIT_PASSWORD=${REDDIT_PASSWORD}
447
+ # Optional OAuth settings
448
+ - OAUTH_ENABLED=${OAUTH_ENABLED:-false}
449
+ - OAUTH_TOKEN=${OAUTH_TOKEN}
450
+ restart: unless-stopped
132
451
  ```
133
452
 
134
453
  ## 📚 Credits
package/dist/bin.js CHANGED
@@ -30,13 +30,66 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  mod
31
31
  ));
32
32
 
33
- // node_modules/.pnpm/tsup@8.5.0_postcss@8.5.6_typescript@5.8.3/node_modules/tsup/assets/cjs_shims.js
33
+ // node_modules/.pnpm/tsup@8.5.0_postcss@8.5.6_tsx@4.20.3_typescript@5.8.3/node_modules/tsup/assets/cjs_shims.js
34
34
  var init_cjs_shims = __esm({
35
- "node_modules/.pnpm/tsup@8.5.0_postcss@8.5.6_typescript@5.8.3/node_modules/tsup/assets/cjs_shims.js"() {
35
+ "node_modules/.pnpm/tsup@8.5.0_postcss@8.5.6_tsx@4.20.3_typescript@5.8.3/node_modules/tsup/assets/cjs_shims.js"() {
36
36
  "use strict";
37
37
  }
38
38
  });
39
39
 
40
+ // src/middleware/auth.ts
41
+ var auth_exports = {};
42
+ __export(auth_exports, {
43
+ createAuthMiddleware: () => createAuthMiddleware,
44
+ generateRandomToken: () => generateRandomToken
45
+ });
46
+ function createAuthMiddleware(config = {}) {
47
+ return async (c, next) => {
48
+ if (!config.enabled || !config.token) {
49
+ return next();
50
+ }
51
+ const authHeader = c.req.header("Authorization");
52
+ if (!authHeader) {
53
+ throw new import_http_exception.HTTPException(401, {
54
+ message: "Authorization header required"
55
+ });
56
+ }
57
+ const [scheme, token] = authHeader.split(" ");
58
+ if (scheme !== "Bearer") {
59
+ throw new import_http_exception.HTTPException(401, {
60
+ message: "Invalid authorization scheme. Use 'Bearer <token>'"
61
+ });
62
+ }
63
+ if (!token) {
64
+ throw new import_http_exception.HTTPException(401, {
65
+ message: "Bearer token required"
66
+ });
67
+ }
68
+ if (token !== config.token) {
69
+ throw new import_http_exception.HTTPException(403, {
70
+ message: "Invalid token"
71
+ });
72
+ }
73
+ return next();
74
+ };
75
+ }
76
+ function generateRandomToken(length = 32) {
77
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
78
+ let result = "";
79
+ for (let i = 0; i < length; i++) {
80
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
81
+ }
82
+ return result;
83
+ }
84
+ var import_http_exception;
85
+ var init_auth = __esm({
86
+ "src/middleware/auth.ts"() {
87
+ "use strict";
88
+ init_cjs_shims();
89
+ import_http_exception = require("hono/http-exception");
90
+ }
91
+ });
92
+
40
93
  // src/client/reddit-client.ts
41
94
  function initializeRedditClient(config) {
42
95
  redditClient = new RedditClient(config);
@@ -181,7 +234,7 @@ var init_reddit_client = __esm({
181
234
  description: data.description || "",
182
235
  publicDescription: data.public_description || "",
183
236
  subscribers: data.subscribers,
184
- activeUserCount: data.active_user_count,
237
+ activeUserCount: data.active_user_count ?? void 0,
185
238
  createdUtc: data.created_utc,
186
239
  over18: data.over18,
187
240
  subredditType: data.subreddit_type,
@@ -221,7 +274,7 @@ var init_reddit_client = __esm({
221
274
  spoiler: post.spoiler,
222
275
  edited: !!post.edited,
223
276
  isSelf: post.is_self,
224
- linkFlairText: post.link_flair_text,
277
+ linkFlairText: post.link_flair_text ?? void 0,
225
278
  permalink: post.permalink
226
279
  };
227
280
  });
@@ -404,7 +457,7 @@ var init_reddit_client = __esm({
404
457
  spoiler: post.spoiler,
405
458
  edited: !!post.edited,
406
459
  isSelf: post.is_self,
407
- linkFlairText: post.link_flair_text,
460
+ linkFlairText: post.link_flair_text ?? void 0,
408
461
  permalink: post.permalink
409
462
  };
410
463
  });
@@ -508,7 +561,7 @@ var init_reddit_client = __esm({
508
561
  spoiler: post.spoiler,
509
562
  edited: !!post.edited,
510
563
  isSelf: post.is_self,
511
- linkFlairText: post.link_flair_text,
564
+ linkFlairText: post.link_flair_text ?? void 0,
512
565
  permalink: post.permalink
513
566
  };
514
567
  });
@@ -1787,6 +1840,26 @@ if (args.includes("--version") || args.includes("-v")) {
1787
1840
  console.log(packageJson.version);
1788
1841
  process.exit(0);
1789
1842
  }
1843
+ if (args.includes("--generate-token")) {
1844
+ async function generateToken() {
1845
+ const { generateRandomToken: generateRandomToken2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
1846
+ const token = generateRandomToken2(32);
1847
+ console.log(`Generated OAuth token: ${token}`);
1848
+ console.log(`
1849
+ To use this token, set the environment variable:`);
1850
+ console.log(`export OAUTH_TOKEN="${token}"`);
1851
+ console.log(`export OAUTH_ENABLED=true`);
1852
+ console.log(`
1853
+ Then start the HTTP server with: pnpm serve`);
1854
+ process.exit(0);
1855
+ }
1856
+ generateToken().catch((error) => {
1857
+ console.error("Failed to generate token:", error);
1858
+ process.exit(1);
1859
+ });
1860
+ } else {
1861
+ main().then();
1862
+ }
1790
1863
  if (args.includes("--help") || args.includes("-h")) {
1791
1864
  console.log(`
1792
1865
  Reddit MCP Server v${packageJson.version}
@@ -1794,8 +1867,9 @@ Reddit MCP Server v${packageJson.version}
1794
1867
  Usage: reddit-mcp-server [options]
1795
1868
 
1796
1869
  Options:
1797
- -v, --version Show version number
1798
- -h, --help Show help
1870
+ -v, --version Show version number
1871
+ -h, --help Show help
1872
+ --generate-token Generate a secure OAuth token for HTTP server
1799
1873
 
1800
1874
  Environment Variables:
1801
1875
  REDDIT_CLIENT_ID Reddit API client ID (required)
@@ -1804,6 +1878,10 @@ Environment Variables:
1804
1878
  REDDIT_PASSWORD Reddit password (optional, for write operations)
1805
1879
  REDDIT_USER_AGENT Custom user agent (optional)
1806
1880
 
1881
+ HTTP Server OAuth Variables:
1882
+ OAUTH_ENABLED Set to "true" to enable OAuth protection
1883
+ OAUTH_TOKEN Custom OAuth token (use --generate-token to create one)
1884
+
1807
1885
  For more information, visit: https://github.com/jordanburke/reddit-mcp-server
1808
1886
  `);
1809
1887
  process.exit(0);
@@ -1816,4 +1894,3 @@ async function main() {
1816
1894
  process.exit(1);
1817
1895
  });
1818
1896
  }
1819
- main().then();
package/dist/index.js CHANGED
@@ -169,7 +169,7 @@ var RedditClient = class {
169
169
  description: data.description || "",
170
170
  publicDescription: data.public_description || "",
171
171
  subscribers: data.subscribers,
172
- activeUserCount: data.active_user_count,
172
+ activeUserCount: data.active_user_count ?? void 0,
173
173
  createdUtc: data.created_utc,
174
174
  over18: data.over18,
175
175
  subredditType: data.subreddit_type,
@@ -209,7 +209,7 @@ var RedditClient = class {
209
209
  spoiler: post.spoiler,
210
210
  edited: !!post.edited,
211
211
  isSelf: post.is_self,
212
- linkFlairText: post.link_flair_text,
212
+ linkFlairText: post.link_flair_text ?? void 0,
213
213
  permalink: post.permalink
214
214
  };
215
215
  });
@@ -392,7 +392,7 @@ var RedditClient = class {
392
392
  spoiler: post.spoiler,
393
393
  edited: !!post.edited,
394
394
  isSelf: post.is_self,
395
- linkFlairText: post.link_flair_text,
395
+ linkFlairText: post.link_flair_text ?? void 0,
396
396
  permalink: post.permalink
397
397
  };
398
398
  });
@@ -496,7 +496,7 @@ var RedditClient = class {
496
496
  spoiler: post.spoiler,
497
497
  edited: !!post.edited,
498
498
  isSelf: post.is_self,
499
- linkFlairText: post.link_flair_text,
499
+ linkFlairText: post.link_flair_text ?? void 0,
500
500
  permalink: post.permalink
501
501
  };
502
502
  });
@@ -0,0 +1,5 @@
1
+ import * as _hono_node_server from '@hono/node-server';
2
+
3
+ declare function startServer(port?: number): _hono_node_server.ServerType;
4
+
5
+ export { startServer };