reddit-mcp-server 1.1.0 → 1.1.2
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/CHANGELOG.md +8 -2
- package/README.md +91 -62
- package/dist/bin.js +728 -1028
- package/dist/index.d.ts +1 -8
- package/dist/index.js +703 -873
- package/package.json +35 -28
- package/dist/server.d.ts +0 -5
- package/dist/server.js +0 -1754
package/dist/bin.js
CHANGED
|
@@ -9,10 +9,6 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
|
9
9
|
var __esm = (fn, res) => function __init() {
|
|
10
10
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
11
|
};
|
|
12
|
-
var __export = (target, all) => {
|
|
13
|
-
for (var name in all)
|
|
14
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
-
};
|
|
16
12
|
var __copyProps = (to, from, except, desc) => {
|
|
17
13
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
14
|
for (let key of __getOwnPropNames(from))
|
|
@@ -30,63 +26,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
26
|
mod
|
|
31
27
|
));
|
|
32
28
|
|
|
33
|
-
// node_modules/.pnpm/tsup@8.5.
|
|
29
|
+
// node_modules/.pnpm/tsup@8.5.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js
|
|
34
30
|
var init_cjs_shims = __esm({
|
|
35
|
-
"node_modules/.pnpm/tsup@8.5.
|
|
36
|
-
"use strict";
|
|
37
|
-
}
|
|
38
|
-
});
|
|
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"() {
|
|
31
|
+
"node_modules/.pnpm/tsup@8.5.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js"() {
|
|
87
32
|
"use strict";
|
|
88
|
-
init_cjs_shims();
|
|
89
|
-
import_http_exception = require("hono/http-exception");
|
|
90
33
|
}
|
|
91
34
|
});
|
|
92
35
|
|
|
@@ -155,7 +98,8 @@ var init_reddit_client = __esm({
|
|
|
155
98
|
}
|
|
156
99
|
const authUrl = "https://www.reddit.com/api/v1/access_token";
|
|
157
100
|
const authData = new URLSearchParams();
|
|
158
|
-
|
|
101
|
+
const isUserAuth = !!(this.username && this.password);
|
|
102
|
+
if (isUserAuth) {
|
|
159
103
|
authData.append("grant_type", "password");
|
|
160
104
|
authData.append("username", this.username);
|
|
161
105
|
authData.append("password", this.password);
|
|
@@ -173,13 +117,17 @@ var init_reddit_client = __esm({
|
|
|
173
117
|
body: authData.toString()
|
|
174
118
|
});
|
|
175
119
|
if (!response.ok) {
|
|
176
|
-
|
|
120
|
+
const statusText = response.statusText || "Unknown Error";
|
|
121
|
+
throw new Error(`Authentication failed: ${response.status} ${statusText}`);
|
|
177
122
|
}
|
|
178
123
|
const data = await response.json();
|
|
179
124
|
this.accessToken = data.access_token;
|
|
180
125
|
this.tokenExpiry = now + data.expires_in * 1e3;
|
|
181
126
|
this.authenticated = true;
|
|
182
|
-
} catch {
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if (error instanceof Error) {
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
183
131
|
throw new Error("Failed to authenticate with Reddit API");
|
|
184
132
|
}
|
|
185
133
|
}
|
|
@@ -337,6 +285,7 @@ var init_reddit_client = __esm({
|
|
|
337
285
|
}
|
|
338
286
|
}
|
|
339
287
|
async createPost(subreddit, title, content, isSelf = true) {
|
|
288
|
+
var _a, _b, _c, _d, _e, _f;
|
|
340
289
|
await this.authenticate();
|
|
341
290
|
if (!this.username || !this.password) {
|
|
342
291
|
throw new Error("User authentication required for posting");
|
|
@@ -348,6 +297,7 @@ var init_reddit_client = __esm({
|
|
|
348
297
|
params.append("kind", kind);
|
|
349
298
|
params.append("title", title);
|
|
350
299
|
params.append(isSelf ? "text" : "url", content);
|
|
300
|
+
params.append("api_type", "json");
|
|
351
301
|
const response = await this.makeRequest("/api/submit", {
|
|
352
302
|
method: "POST",
|
|
353
303
|
headers: {
|
|
@@ -356,17 +306,33 @@ var init_reddit_client = __esm({
|
|
|
356
306
|
body: params.toString()
|
|
357
307
|
});
|
|
358
308
|
if (!response.ok) {
|
|
359
|
-
|
|
309
|
+
const errorText = await response.text();
|
|
310
|
+
console.error(`[Reddit API] Create post failed: ${response.status} ${response.statusText}`);
|
|
311
|
+
console.error(`[Reddit API] Error response: ${errorText}`);
|
|
312
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
360
313
|
}
|
|
361
314
|
const json = await response.json();
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
throw new Error(
|
|
315
|
+
console.error(`[Reddit API] Create post response:`, JSON.stringify(json, null, 2));
|
|
316
|
+
if (((_a = json.json) == null ? void 0 : _a.errors) && json.json.errors.length > 0) {
|
|
317
|
+
const errors = json.json.errors.map((e) => e.join(": ")).join(", ");
|
|
318
|
+
console.error(`[Reddit API] Post creation errors: ${errors}`);
|
|
319
|
+
throw new Error(`Reddit API errors: ${errors}`);
|
|
367
320
|
}
|
|
368
|
-
|
|
369
|
-
|
|
321
|
+
const postId = ((_c = (_b = json.json) == null ? void 0 : _b.data) == null ? void 0 : _c.id) || ((_f = (_e = (_d = json.json) == null ? void 0 : _d.data) == null ? void 0 : _e.name) == null ? void 0 : _f.replace("t3_", ""));
|
|
322
|
+
if (!postId) {
|
|
323
|
+
console.error(`[Reddit API] No post ID in response`);
|
|
324
|
+
throw new Error("No post ID returned from Reddit");
|
|
325
|
+
}
|
|
326
|
+
console.error(`[Reddit API] Post created with ID: ${postId}`);
|
|
327
|
+
return await this.getPost(postId, subreddit);
|
|
328
|
+
} catch (error) {
|
|
329
|
+
console.error(`[Reddit API] Create post exception:`, error);
|
|
330
|
+
if (error instanceof Error && error.message.includes("HTTP")) {
|
|
331
|
+
throw error;
|
|
332
|
+
}
|
|
333
|
+
throw new Error(
|
|
334
|
+
`Failed to create post in ${subreddit}: ${error instanceof Error ? error.message : String(error)}`
|
|
335
|
+
);
|
|
370
336
|
}
|
|
371
337
|
}
|
|
372
338
|
async checkPostExists(postId) {
|
|
@@ -394,6 +360,7 @@ var init_reddit_client = __esm({
|
|
|
394
360
|
const params = new URLSearchParams();
|
|
395
361
|
params.append("thing_id", `t3_${postId}`);
|
|
396
362
|
params.append("text", content);
|
|
363
|
+
params.append("api_type", "json");
|
|
397
364
|
const response = await this.makeRequest("/api/comment", {
|
|
398
365
|
method: "POST",
|
|
399
366
|
headers: {
|
|
@@ -402,26 +369,126 @@ var init_reddit_client = __esm({
|
|
|
402
369
|
body: params.toString()
|
|
403
370
|
});
|
|
404
371
|
if (!response.ok) {
|
|
405
|
-
|
|
372
|
+
const errorText = await response.text();
|
|
373
|
+
console.error(`[Reddit API] Reply to post failed: ${response.status} ${response.statusText}`);
|
|
374
|
+
console.error(`[Reddit API] Error response: ${errorText}`);
|
|
375
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
406
376
|
}
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
377
|
+
const json = await response.json();
|
|
378
|
+
console.error(`[Reddit API] Reply response:`, JSON.stringify(json, null, 2));
|
|
379
|
+
if (json.json && json.json.data && json.json.data.things) {
|
|
380
|
+
const commentData = json.json.data.things[0].data;
|
|
381
|
+
return {
|
|
382
|
+
id: commentData.id,
|
|
383
|
+
author: this.username,
|
|
384
|
+
body: content,
|
|
385
|
+
score: 1,
|
|
386
|
+
controversiality: 0,
|
|
387
|
+
subreddit: commentData.subreddit,
|
|
388
|
+
submissionTitle: commentData.link_title || "",
|
|
389
|
+
createdUtc: Date.now() / 1e3,
|
|
390
|
+
edited: false,
|
|
391
|
+
isSubmitter: false,
|
|
392
|
+
permalink: commentData.permalink
|
|
393
|
+
};
|
|
394
|
+
} else if (json.json && json.json.errors && json.json.errors.length > 0) {
|
|
395
|
+
const errors = json.json.errors.map((e) => e.join(": ")).join(", ");
|
|
396
|
+
console.error(`[Reddit API] Reply errors: ${errors}`);
|
|
397
|
+
throw new Error(`Reddit API errors: ${errors}`);
|
|
398
|
+
} else {
|
|
399
|
+
console.error(`[Reddit API] Unexpected reply response format`);
|
|
400
|
+
throw new Error("Failed to parse reply response");
|
|
401
|
+
}
|
|
402
|
+
} catch (error) {
|
|
403
|
+
console.error(`[Reddit API] Reply to post exception:`, error);
|
|
404
|
+
if (error instanceof Error && error.message.includes("HTTP")) {
|
|
405
|
+
throw error;
|
|
406
|
+
}
|
|
407
|
+
throw new Error(`Failed to reply to post ${postId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
async deletePost(thingId) {
|
|
411
|
+
await this.authenticate();
|
|
412
|
+
if (!this.username || !this.password) {
|
|
413
|
+
throw new Error("User authentication required for deleting content");
|
|
414
|
+
}
|
|
415
|
+
try {
|
|
416
|
+
const fullThingId = thingId.startsWith("t3_") || thingId.startsWith("t1_") ? thingId : `t3_${thingId}`;
|
|
417
|
+
const params = new URLSearchParams();
|
|
418
|
+
params.append("id", fullThingId);
|
|
419
|
+
const response = await this.makeRequest("/api/del", {
|
|
420
|
+
method: "POST",
|
|
421
|
+
headers: {
|
|
422
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
423
|
+
},
|
|
424
|
+
body: params.toString()
|
|
425
|
+
});
|
|
426
|
+
if (!response.ok) {
|
|
427
|
+
const errorText = await response.text();
|
|
428
|
+
console.error(`[Reddit API] Delete failed: ${response.status} ${response.statusText}`);
|
|
429
|
+
console.error(`[Reddit API] Error response: ${errorText}`);
|
|
430
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
431
|
+
}
|
|
432
|
+
console.error(`[Reddit API] Successfully deleted ${fullThingId}`);
|
|
433
|
+
return true;
|
|
434
|
+
} catch (error) {
|
|
435
|
+
console.error(`[Reddit API] Delete exception:`, error);
|
|
436
|
+
if (error instanceof Error && error.message.includes("HTTP")) {
|
|
437
|
+
throw error;
|
|
438
|
+
}
|
|
439
|
+
throw new Error(`Failed to delete content ${thingId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
423
440
|
}
|
|
424
441
|
}
|
|
442
|
+
async deleteComment(thingId) {
|
|
443
|
+
const fullThingId = thingId.startsWith("t1_") ? thingId : `t1_${thingId}`;
|
|
444
|
+
return this.deletePost(fullThingId);
|
|
445
|
+
}
|
|
446
|
+
async editPost(thingId, newText) {
|
|
447
|
+
var _a;
|
|
448
|
+
await this.authenticate();
|
|
449
|
+
if (!this.username || !this.password) {
|
|
450
|
+
throw new Error("User authentication required for editing content");
|
|
451
|
+
}
|
|
452
|
+
try {
|
|
453
|
+
const fullThingId = thingId.startsWith("t3_") || thingId.startsWith("t1_") ? thingId : `t3_${thingId}`;
|
|
454
|
+
const params = new URLSearchParams();
|
|
455
|
+
params.append("thing_id", fullThingId);
|
|
456
|
+
params.append("text", newText);
|
|
457
|
+
params.append("api_type", "json");
|
|
458
|
+
const response = await this.makeRequest("/api/editusertext", {
|
|
459
|
+
method: "POST",
|
|
460
|
+
headers: {
|
|
461
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
462
|
+
},
|
|
463
|
+
body: params.toString()
|
|
464
|
+
});
|
|
465
|
+
if (!response.ok) {
|
|
466
|
+
const errorText = await response.text();
|
|
467
|
+
console.error(`[Reddit API] Edit failed: ${response.status} ${response.statusText}`);
|
|
468
|
+
console.error(`[Reddit API] Error response: ${errorText}`);
|
|
469
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
470
|
+
}
|
|
471
|
+
const json = await response.json();
|
|
472
|
+
console.error(`[Reddit API] Edit response:`, JSON.stringify(json, null, 2));
|
|
473
|
+
if (((_a = json.json) == null ? void 0 : _a.errors) && json.json.errors.length > 0) {
|
|
474
|
+
const errors = json.json.errors.map((e) => e.join(": ")).join(", ");
|
|
475
|
+
console.error(`[Reddit API] Edit errors: ${errors}`);
|
|
476
|
+
throw new Error(`Reddit API errors: ${errors}`);
|
|
477
|
+
}
|
|
478
|
+
console.error(`[Reddit API] Successfully edited ${fullThingId}`);
|
|
479
|
+
return true;
|
|
480
|
+
} catch (error) {
|
|
481
|
+
console.error(`[Reddit API] Edit exception:`, error);
|
|
482
|
+
if (error instanceof Error && error.message.includes("HTTP")) {
|
|
483
|
+
throw error;
|
|
484
|
+
}
|
|
485
|
+
throw new Error(`Failed to edit content ${thingId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
async editComment(thingId, newText) {
|
|
489
|
+
const fullThingId = thingId.startsWith("t1_") ? thingId : `t1_${thingId}`;
|
|
490
|
+
return this.editPost(fullThingId, newText);
|
|
491
|
+
}
|
|
425
492
|
async searchReddit(query, options = {}) {
|
|
426
493
|
await this.authenticate();
|
|
427
494
|
try {
|
|
@@ -724,21 +791,6 @@ function getSubredditEngagementTips(subreddit) {
|
|
|
724
791
|
}
|
|
725
792
|
return tips.length ? tips.join("\n - ") : "Regular engagement recommended to maintain community presence";
|
|
726
793
|
}
|
|
727
|
-
function analyzeCommentImpact(score, isEdited, isOp) {
|
|
728
|
-
const insights = [];
|
|
729
|
-
if (score > 100) {
|
|
730
|
-
insights.push("Highly upvoted comment with significant community agreement");
|
|
731
|
-
} else if (score < 0) {
|
|
732
|
-
insights.push("Controversial or contested viewpoint");
|
|
733
|
-
}
|
|
734
|
-
if (isEdited) {
|
|
735
|
-
insights.push("Refined for clarity or accuracy");
|
|
736
|
-
}
|
|
737
|
-
if (isOp) {
|
|
738
|
-
insights.push("Author's perspective adds context to original post");
|
|
739
|
-
}
|
|
740
|
-
return insights.length ? insights.join("\n - ") : "Standard engagement with discussion";
|
|
741
|
-
}
|
|
742
794
|
function formatUserInfo(user) {
|
|
743
795
|
const status = [];
|
|
744
796
|
if (user.isMod) status.push("Moderator");
|
|
@@ -819,44 +871,6 @@ function formatSubredditInfo(subreddit) {
|
|
|
819
871
|
engagementTips: getSubredditEngagementTips(subreddit)
|
|
820
872
|
};
|
|
821
873
|
}
|
|
822
|
-
function formatCommentInfo(comment) {
|
|
823
|
-
const flags = [];
|
|
824
|
-
if (comment.edited) flags.push("Edited");
|
|
825
|
-
if (comment.isSubmitter) flags.push("OP");
|
|
826
|
-
return {
|
|
827
|
-
author: comment.author,
|
|
828
|
-
content: comment.body.length > 300 ? comment.body.substring(0, 297) + "..." : comment.body,
|
|
829
|
-
stats: {
|
|
830
|
-
score: comment.score,
|
|
831
|
-
controversiality: comment.controversiality
|
|
832
|
-
},
|
|
833
|
-
context: {
|
|
834
|
-
subreddit: comment.subreddit,
|
|
835
|
-
thread: comment.submissionTitle
|
|
836
|
-
},
|
|
837
|
-
metadata: {
|
|
838
|
-
posted: formatTimestamp(comment.createdUtc),
|
|
839
|
-
flags: flags.length ? flags : ["None"]
|
|
840
|
-
},
|
|
841
|
-
link: `https://reddit.com${comment.permalink}`,
|
|
842
|
-
commentAnalysis: analyzeCommentImpact(comment.score, comment.edited, comment.isSubmitter)
|
|
843
|
-
};
|
|
844
|
-
}
|
|
845
|
-
function formatPost(post) {
|
|
846
|
-
return {
|
|
847
|
-
title: post.title,
|
|
848
|
-
author: post.author,
|
|
849
|
-
subreddit: post.subreddit,
|
|
850
|
-
score: post.score,
|
|
851
|
-
upvoteRatio: Math.round(post.upvoteRatio * 100),
|
|
852
|
-
numComments: post.numComments,
|
|
853
|
-
createdAt: formatTimestamp(post.createdUtc),
|
|
854
|
-
selftext: post.selftext,
|
|
855
|
-
permalink: post.permalink,
|
|
856
|
-
nsfw: post.over18,
|
|
857
|
-
spoiler: post.spoiler
|
|
858
|
-
};
|
|
859
|
-
}
|
|
860
874
|
var init_formatters = __esm({
|
|
861
875
|
"src/utils/formatters.ts"() {
|
|
862
876
|
"use strict";
|
|
@@ -864,22 +878,175 @@ var init_formatters = __esm({
|
|
|
864
878
|
}
|
|
865
879
|
});
|
|
866
880
|
|
|
867
|
-
// src/
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
const
|
|
871
|
-
|
|
872
|
-
|
|
881
|
+
// src/index.ts
|
|
882
|
+
var src_exports = {};
|
|
883
|
+
async function setupRedditClient() {
|
|
884
|
+
const clientId = process.env.REDDIT_CLIENT_ID;
|
|
885
|
+
const clientSecret = process.env.REDDIT_CLIENT_SECRET;
|
|
886
|
+
const userAgent = process.env.REDDIT_USER_AGENT || "RedditMCPServer/1.1.0";
|
|
887
|
+
const username = process.env.REDDIT_USERNAME;
|
|
888
|
+
const password = process.env.REDDIT_PASSWORD;
|
|
889
|
+
if (!clientId || !clientSecret) {
|
|
890
|
+
console.error(
|
|
891
|
+
"[Error] Missing required Reddit API credentials. Please set REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET environment variables."
|
|
892
|
+
);
|
|
893
|
+
process.exit(1);
|
|
894
|
+
}
|
|
895
|
+
try {
|
|
896
|
+
const client = initializeRedditClient({
|
|
897
|
+
clientId,
|
|
898
|
+
clientSecret,
|
|
899
|
+
userAgent,
|
|
900
|
+
username,
|
|
901
|
+
password
|
|
902
|
+
});
|
|
903
|
+
console.error("[Setup] Reddit client initialized");
|
|
904
|
+
console.error("[Setup] Testing Reddit API connection...");
|
|
905
|
+
const isConnected = await client.checkAuthentication();
|
|
906
|
+
if (!isConnected) {
|
|
907
|
+
console.error("[Error] \u2717 Failed to connect to Reddit API");
|
|
908
|
+
console.error("[Error] Please check your REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET");
|
|
909
|
+
process.exit(1);
|
|
910
|
+
}
|
|
911
|
+
console.error("[Setup] \u2713 Reddit API connection successful");
|
|
912
|
+
if (username && password) {
|
|
913
|
+
console.error(`[Setup] \u2713 User authenticated as: ${username}`);
|
|
914
|
+
console.error("[Setup] Write operations enabled (posting, replying, editing, deleting)");
|
|
915
|
+
} else {
|
|
916
|
+
console.error("[Setup] Running in read-only mode (client credentials only)");
|
|
917
|
+
console.error("[Setup] For write operations, set REDDIT_USERNAME and REDDIT_PASSWORD");
|
|
918
|
+
}
|
|
919
|
+
} catch (error) {
|
|
920
|
+
console.error("[Error] \u2717 Reddit API connection failed:", error instanceof Error ? error.message : error);
|
|
921
|
+
console.error("[Error] Please verify your Reddit API credentials");
|
|
922
|
+
process.exit(1);
|
|
873
923
|
}
|
|
924
|
+
}
|
|
925
|
+
async function main() {
|
|
874
926
|
try {
|
|
875
|
-
|
|
876
|
-
const
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
927
|
+
await setupRedditClient();
|
|
928
|
+
const useStdio = process.env.TRANSPORT_TYPE === "stdio";
|
|
929
|
+
const port = parseInt(process.env.PORT || "3000");
|
|
930
|
+
const host = process.env.HOST || "0.0.0.0";
|
|
931
|
+
if (useStdio) {
|
|
932
|
+
console.error("[Setup] Starting in stdio mode (CLI/npx)");
|
|
933
|
+
await server.start({
|
|
934
|
+
transportType: "stdio"
|
|
935
|
+
});
|
|
936
|
+
} else {
|
|
937
|
+
console.error(`[Setup] Starting HTTP server on ${host}:${port}`);
|
|
938
|
+
await server.start({
|
|
939
|
+
transportType: "httpStream",
|
|
940
|
+
httpStream: {
|
|
941
|
+
port,
|
|
942
|
+
host,
|
|
943
|
+
endpoint: "/mcp"
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
console.error(`[Setup] HTTP server ready at http://${host}:${port}/mcp`);
|
|
947
|
+
console.error(`[Setup] SSE endpoint available at http://${host}:${port}/sse`);
|
|
948
|
+
}
|
|
949
|
+
} catch (error) {
|
|
950
|
+
console.error("[Error] Failed to start server:", error);
|
|
951
|
+
process.exit(1);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
var import_fastmcp, import_zod, import_dotenv, server;
|
|
955
|
+
var init_src = __esm({
|
|
956
|
+
"src/index.ts"() {
|
|
957
|
+
"use strict";
|
|
958
|
+
init_cjs_shims();
|
|
959
|
+
import_fastmcp = require("fastmcp");
|
|
960
|
+
import_zod = require("zod");
|
|
961
|
+
init_reddit_client();
|
|
962
|
+
init_formatters();
|
|
963
|
+
import_dotenv = __toESM(require("dotenv"));
|
|
964
|
+
import_dotenv.default.config();
|
|
965
|
+
server = new import_fastmcp.FastMCP({
|
|
966
|
+
name: "reddit-mcp-server",
|
|
967
|
+
version: "1.1.0",
|
|
968
|
+
instructions: `A comprehensive Reddit MCP server that provides tools for interacting with Reddit API.
|
|
969
|
+
|
|
970
|
+
Available capabilities:
|
|
971
|
+
- Fetch Reddit posts, comments, and user information
|
|
972
|
+
- Get subreddit details and statistics
|
|
973
|
+
- Search Reddit content across posts and subreddits
|
|
974
|
+
- Create posts and reply to posts/comments (with authentication)
|
|
975
|
+
- Edit your own posts and comments (with authentication)
|
|
976
|
+
- Delete your own posts and comments (with authentication)
|
|
977
|
+
- Analyze engagement metrics and community insights
|
|
978
|
+
|
|
979
|
+
For write operations (posting, replying, editing, deleting), ensure REDDIT_USERNAME and REDDIT_PASSWORD are configured.`,
|
|
980
|
+
// Optional OAuth configuration for HTTP transport
|
|
981
|
+
...process.env.OAUTH_ENABLED === "true" && {
|
|
982
|
+
authenticate: async (request) => {
|
|
983
|
+
const authHeader = request.headers.authorization;
|
|
984
|
+
const expectedToken = process.env.OAUTH_TOKEN;
|
|
985
|
+
if (!expectedToken) {
|
|
986
|
+
const token2 = Array.from(
|
|
987
|
+
{ length: 32 },
|
|
988
|
+
() => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".charAt(Math.floor(Math.random() * 62))
|
|
989
|
+
).join("");
|
|
990
|
+
console.log(`[Auth] Generated OAuth token: ${token2}`);
|
|
991
|
+
throw new Response(
|
|
992
|
+
JSON.stringify({
|
|
993
|
+
error: "No OAuth token configured",
|
|
994
|
+
generatedToken: token2
|
|
995
|
+
}),
|
|
996
|
+
{
|
|
997
|
+
status: 401,
|
|
998
|
+
headers: { "Content-Type": "application/json" }
|
|
999
|
+
}
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
if (!(authHeader == null ? void 0 : authHeader.startsWith("Bearer "))) {
|
|
1003
|
+
throw new Response(null, {
|
|
1004
|
+
status: 401,
|
|
1005
|
+
statusText: "Missing or invalid Authorization header"
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
const token = authHeader.slice(7);
|
|
1009
|
+
if (token !== expectedToken) {
|
|
1010
|
+
throw new Response(null, {
|
|
1011
|
+
status: 403,
|
|
1012
|
+
statusText: "Invalid token"
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
return { authenticated: true };
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
server.addTool({
|
|
1020
|
+
name: "test_reddit_mcp_server",
|
|
1021
|
+
description: "Test the Reddit MCP Server connection and configuration",
|
|
1022
|
+
parameters: import_zod.z.object({}),
|
|
1023
|
+
execute: async () => {
|
|
1024
|
+
const client = getRedditClient();
|
|
1025
|
+
const hasAuth = client ? "\u2713" : "\u2717";
|
|
1026
|
+
const hasWriteAccess = process.env.REDDIT_USERNAME && process.env.REDDIT_PASSWORD ? "\u2713" : "\u2717";
|
|
1027
|
+
return `Reddit MCP Server Status:
|
|
1028
|
+
- Server: \u2713 Running
|
|
1029
|
+
- Reddit Client: ${hasAuth} ${client ? "Initialized" : "Not initialized"}
|
|
1030
|
+
- Write Access: ${hasWriteAccess} ${hasWriteAccess === "\u2713" ? "Available" : "Read-only mode"}
|
|
1031
|
+
- Version: 1.1.0
|
|
1032
|
+
|
|
1033
|
+
Ready to handle Reddit API requests!`;
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
server.addTool({
|
|
1037
|
+
name: "get_user_info",
|
|
1038
|
+
description: "Get detailed information about a Reddit user including karma, account status, and activity analysis",
|
|
1039
|
+
parameters: import_zod.z.object({
|
|
1040
|
+
username: import_zod.z.string().describe("The Reddit username (without u/ prefix)")
|
|
1041
|
+
}),
|
|
1042
|
+
execute: async (args2) => {
|
|
1043
|
+
const client = getRedditClient();
|
|
1044
|
+
if (!client) {
|
|
1045
|
+
throw new Error("Reddit client not initialized");
|
|
1046
|
+
}
|
|
1047
|
+
const user = await client.getUser(args2.username);
|
|
1048
|
+
const formattedUser = formatUserInfo(user);
|
|
1049
|
+
return `# User Information: u/${formattedUser.username}
|
|
883
1050
|
|
|
884
1051
|
## Profile Overview
|
|
885
1052
|
- Username: u/${formattedUser.username}
|
|
@@ -895,122 +1062,99 @@ async function getUserInfo(params) {
|
|
|
895
1062
|
- ${formattedUser.activityAnalysis.replace(/\n - /g, "\n- ")}
|
|
896
1063
|
|
|
897
1064
|
## Recommendations
|
|
898
|
-
- ${formattedUser.recommendations.replace(/\n
|
|
899
|
-
|
|
900
|
-
}
|
|
901
|
-
]
|
|
902
|
-
};
|
|
903
|
-
} catch (error) {
|
|
904
|
-
throw new import_types.McpError(import_types.ErrorCode.InternalError, `Failed to fetch user data: ${String(error)}`);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
async function getUserPosts(params) {
|
|
908
|
-
const { username, sort = "new", time_filter = "all", limit = 10 } = params;
|
|
909
|
-
const client = getRedditClient();
|
|
910
|
-
if (!client) {
|
|
911
|
-
throw new import_types.McpError(import_types.ErrorCode.InternalError, "Reddit client not initialized");
|
|
912
|
-
}
|
|
913
|
-
try {
|
|
914
|
-
const posts = await client.getUserPosts(username, {
|
|
915
|
-
sort,
|
|
916
|
-
timeFilter: time_filter,
|
|
917
|
-
limit
|
|
1065
|
+
- ${formattedUser.recommendations.replace(/\n {2}- /g, "\n- ")}`;
|
|
1066
|
+
}
|
|
918
1067
|
});
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
return `### ${index + 1}. ${post.title}
|
|
933
|
-
- Subreddit: r/${post.subreddit}
|
|
934
|
-
- Score: ${post.score} (${Math.round(post.upvoteRatio * 100)}% upvoted)
|
|
935
|
-
- Comments: ${post.numComments}
|
|
936
|
-
- Posted: ${date}
|
|
937
|
-
${selftext}
|
|
938
|
-
- Link: https://reddit.com${post.permalink}
|
|
939
|
-
${post.over18 ? "- **NSFW**" : ""}
|
|
940
|
-
${post.spoiler ? "- **Spoiler**" : ""}`;
|
|
941
|
-
}).join("\n\n---\n\n")}`
|
|
1068
|
+
server.addTool({
|
|
1069
|
+
name: "get_user_posts",
|
|
1070
|
+
description: "Get recent posts by a Reddit user with sorting and filtering options",
|
|
1071
|
+
parameters: import_zod.z.object({
|
|
1072
|
+
username: import_zod.z.string().describe("The Reddit username (without u/ prefix)"),
|
|
1073
|
+
sort: import_zod.z.enum(["new", "hot", "top"]).default("new").describe("Sort order for posts"),
|
|
1074
|
+
time_filter: import_zod.z.enum(["hour", "day", "week", "month", "year", "all"]).default("all").describe("Time filter for top posts"),
|
|
1075
|
+
limit: import_zod.z.number().min(1).max(100).default(10).describe("Number of posts to retrieve")
|
|
1076
|
+
}),
|
|
1077
|
+
execute: async (args2) => {
|
|
1078
|
+
const client = getRedditClient();
|
|
1079
|
+
if (!client) {
|
|
1080
|
+
throw new Error("Reddit client not initialized");
|
|
942
1081
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1082
|
+
const posts = await client.getUserPosts(args2.username, {
|
|
1083
|
+
sort: args2.sort,
|
|
1084
|
+
timeFilter: args2.time_filter,
|
|
1085
|
+
limit: args2.limit
|
|
1086
|
+
});
|
|
1087
|
+
if (posts.length === 0) {
|
|
1088
|
+
return `No posts found for u/${args2.username} with the specified filters.`;
|
|
1089
|
+
}
|
|
1090
|
+
const postSummaries = posts.map((post, index) => {
|
|
1091
|
+
const flags = [...post.over18 ? ["**NSFW**"] : [], ...post.spoiler ? ["**Spoiler**"] : []];
|
|
1092
|
+
return `### ${index + 1}. ${post.title} ${flags.join(" ")}
|
|
1093
|
+
- Subreddit: r/${post.subreddit}
|
|
1094
|
+
- Score: ${post.score.toLocaleString()} (${(post.upvoteRatio * 100).toFixed(1)}% upvoted)
|
|
1095
|
+
- Comments: ${post.numComments.toLocaleString()}
|
|
1096
|
+
- Posted: ${new Date(post.createdUtc * 1e3).toLocaleString()}
|
|
1097
|
+
- Link: https://reddit.com${post.permalink}`;
|
|
1098
|
+
}).join("\n\n");
|
|
1099
|
+
return `# Posts by u/${args2.username} (${args2.sort} - ${args2.time_filter})
|
|
1100
|
+
|
|
1101
|
+
${postSummaries}`;
|
|
1102
|
+
}
|
|
960
1103
|
});
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1104
|
+
server.addTool({
|
|
1105
|
+
name: "get_user_comments",
|
|
1106
|
+
description: "Get recent comments by a Reddit user with sorting and filtering options",
|
|
1107
|
+
parameters: import_zod.z.object({
|
|
1108
|
+
username: import_zod.z.string().describe("The Reddit username (without u/ prefix)"),
|
|
1109
|
+
sort: import_zod.z.enum(["new", "hot", "top"]).default("new").describe("Sort order for comments"),
|
|
1110
|
+
time_filter: import_zod.z.enum(["hour", "day", "week", "month", "year", "all"]).default("all").describe("Time filter for top comments"),
|
|
1111
|
+
limit: import_zod.z.number().min(1).max(100).default(10).describe("Number of comments to retrieve")
|
|
1112
|
+
}),
|
|
1113
|
+
execute: async (args2) => {
|
|
1114
|
+
const client = getRedditClient();
|
|
1115
|
+
if (!client) {
|
|
1116
|
+
throw new Error("Reddit client not initialized");
|
|
1117
|
+
}
|
|
1118
|
+
const comments = await client.getUserComments(args2.username, {
|
|
1119
|
+
sort: args2.sort,
|
|
1120
|
+
timeFilter: args2.time_filter,
|
|
1121
|
+
limit: args2.limit
|
|
1122
|
+
});
|
|
1123
|
+
if (comments.length === 0) {
|
|
1124
|
+
return `No comments found for u/${args2.username} with the specified filters.`;
|
|
1125
|
+
}
|
|
1126
|
+
const commentSummaries = comments.map((comment, index) => {
|
|
1127
|
+
const truncatedBody = comment.body.length > 300 ? comment.body.substring(0, 300) + "..." : comment.body;
|
|
1128
|
+
const flags = [...comment.edited ? ["*(edited)*"] : [], ...comment.isSubmitter ? ["**OP**"] : []];
|
|
1129
|
+
return `### ${index + 1}. Comment ${flags.join(" ")}
|
|
1130
|
+
In r/${comment.subreddit} on "${comment.submissionTitle}"
|
|
966
1131
|
|
|
967
|
-
|
|
1132
|
+
> ${truncatedBody}
|
|
968
1133
|
|
|
969
|
-
${
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
- Score: ${comment.score} points
|
|
975
|
-
- Posted: ${date}${edited}
|
|
976
|
-
- Link: https://reddit.com${comment.permalink}
|
|
1134
|
+
- Score: ${comment.score.toLocaleString()}
|
|
1135
|
+
- Posted: ${new Date(comment.createdUtc * 1e3).toLocaleString()}
|
|
1136
|
+
- Link: https://reddit.com${comment.permalink}`;
|
|
1137
|
+
}).join("\n\n");
|
|
1138
|
+
return `# Comments by u/${args2.username} (${args2.sort} - ${args2.time_filter})
|
|
977
1139
|
|
|
978
|
-
${
|
|
979
|
-
|
|
1140
|
+
${commentSummaries}`;
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
server.addTool({
|
|
1144
|
+
name: "get_reddit_post",
|
|
1145
|
+
description: "Get detailed information about a specific Reddit post including content, stats, and engagement analysis",
|
|
1146
|
+
parameters: import_zod.z.object({
|
|
1147
|
+
subreddit: import_zod.z.string().describe("The subreddit name (without r/ prefix)"),
|
|
1148
|
+
post_id: import_zod.z.string().describe("The Reddit post ID")
|
|
1149
|
+
}),
|
|
1150
|
+
execute: async (args2) => {
|
|
1151
|
+
const client = getRedditClient();
|
|
1152
|
+
if (!client) {
|
|
1153
|
+
throw new Error("Reddit client not initialized");
|
|
980
1154
|
}
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
throw new import_types.McpError(import_types.ErrorCode.InternalError, `Failed to fetch user comments: ${String(error)}`);
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
var import_types;
|
|
988
|
-
var init_user_tools = __esm({
|
|
989
|
-
"src/tools/user-tools.ts"() {
|
|
990
|
-
"use strict";
|
|
991
|
-
init_cjs_shims();
|
|
992
|
-
init_reddit_client();
|
|
993
|
-
init_formatters();
|
|
994
|
-
import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
995
|
-
}
|
|
996
|
-
});
|
|
997
|
-
|
|
998
|
-
// src/tools/post-tools.ts
|
|
999
|
-
async function getRedditPost(params) {
|
|
1000
|
-
const { subreddit, post_id } = params;
|
|
1001
|
-
const client = getRedditClient();
|
|
1002
|
-
if (!client) {
|
|
1003
|
-
throw new import_types2.McpError(import_types2.ErrorCode.InternalError, "Reddit client not initialized");
|
|
1004
|
-
}
|
|
1005
|
-
try {
|
|
1006
|
-
const post = await client.getPost(post_id, subreddit);
|
|
1007
|
-
const formattedPost = formatPostInfo(post);
|
|
1008
|
-
return {
|
|
1009
|
-
content: [
|
|
1010
|
-
{
|
|
1011
|
-
type: "text",
|
|
1012
|
-
text: `
|
|
1013
|
-
# Post from r/${formattedPost.subreddit}
|
|
1155
|
+
const post = await client.getPost(args2.post_id, args2.subreddit);
|
|
1156
|
+
const formattedPost = formatPostInfo(post);
|
|
1157
|
+
return `# Post from r/${formattedPost.subreddit}
|
|
1014
1158
|
|
|
1015
1159
|
## Post Details
|
|
1016
1160
|
- Title: ${formattedPost.title}
|
|
@@ -1038,139 +1182,56 @@ ${formattedPost.content}
|
|
|
1038
1182
|
- ${formattedPost.engagementAnalysis.replace(/\n - /g, "\n- ")}
|
|
1039
1183
|
|
|
1040
1184
|
## Best Time to Engage
|
|
1041
|
-
${formattedPost.bestTimeToEngage}
|
|
1042
|
-
|
|
1185
|
+
${formattedPost.bestTimeToEngage}`;
|
|
1186
|
+
}
|
|
1187
|
+
});
|
|
1188
|
+
server.addTool({
|
|
1189
|
+
name: "get_top_posts",
|
|
1190
|
+
description: "Get top posts from a subreddit or from the Reddit home feed",
|
|
1191
|
+
parameters: import_zod.z.object({
|
|
1192
|
+
subreddit: import_zod.z.string().optional().describe("The subreddit name (without r/ prefix). Leave empty for home feed"),
|
|
1193
|
+
time_filter: import_zod.z.enum(["hour", "day", "week", "month", "year", "all"]).default("week").describe("Time period for top posts"),
|
|
1194
|
+
limit: import_zod.z.number().min(1).max(100).default(10).describe("Number of posts to retrieve")
|
|
1195
|
+
}),
|
|
1196
|
+
execute: async (args2) => {
|
|
1197
|
+
const client = getRedditClient();
|
|
1198
|
+
if (!client) {
|
|
1199
|
+
throw new Error("Reddit client not initialized");
|
|
1043
1200
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
const client = getRedditClient();
|
|
1053
|
-
if (!client) {
|
|
1054
|
-
throw new import_types2.McpError(import_types2.ErrorCode.InternalError, "Reddit client not initialized");
|
|
1055
|
-
}
|
|
1056
|
-
try {
|
|
1057
|
-
const posts = await client.getTopPosts(subreddit, time_filter, limit);
|
|
1058
|
-
const formattedPosts = posts.map(formatPostInfo);
|
|
1059
|
-
const postSummaries = formattedPosts.map(
|
|
1060
|
-
(post, index) => `
|
|
1061
|
-
### ${index + 1}. ${post.title}
|
|
1201
|
+
const posts = await client.getTopPosts(args2.subreddit || "", args2.time_filter, args2.limit);
|
|
1202
|
+
if (posts.length === 0) {
|
|
1203
|
+
const location2 = args2.subreddit ? `r/${args2.subreddit}` : "home feed";
|
|
1204
|
+
return `No posts found in ${location2} for the specified time period.`;
|
|
1205
|
+
}
|
|
1206
|
+
const formattedPosts = posts.map(formatPostInfo);
|
|
1207
|
+
const postSummaries = formattedPosts.map(
|
|
1208
|
+
(post, index) => `### ${index + 1}. ${post.title}
|
|
1062
1209
|
- Author: u/${post.author}
|
|
1063
1210
|
- Score: ${post.stats.score.toLocaleString()} (${(post.stats.upvoteRatio * 100).toFixed(1)}% upvoted)
|
|
1064
1211
|
- Comments: ${post.stats.comments.toLocaleString()}
|
|
1065
1212
|
- Posted: ${post.metadata.posted}
|
|
1066
|
-
- Link: ${post.links.shortLink}
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
content: [
|
|
1071
|
-
{
|
|
1072
|
-
type: "text",
|
|
1073
|
-
text: `
|
|
1074
|
-
# Top Posts from r/${subreddit} (${time_filter})
|
|
1075
|
-
|
|
1076
|
-
${postSummaries}
|
|
1077
|
-
`
|
|
1078
|
-
}
|
|
1079
|
-
]
|
|
1080
|
-
};
|
|
1081
|
-
} catch (error) {
|
|
1082
|
-
throw new import_types2.McpError(import_types2.ErrorCode.InternalError, `Failed to fetch top posts: ${String(error)}`);
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
async function createPost(params) {
|
|
1086
|
-
const { subreddit, title, content, is_self = true } = params;
|
|
1087
|
-
const client = getRedditClient();
|
|
1088
|
-
if (!client) {
|
|
1089
|
-
throw new import_types2.McpError(import_types2.ErrorCode.InternalError, "Reddit client not initialized");
|
|
1090
|
-
}
|
|
1091
|
-
try {
|
|
1092
|
-
const post = await client.createPost(subreddit, title, content, is_self);
|
|
1093
|
-
const formattedPost = formatPostInfo(post);
|
|
1094
|
-
return {
|
|
1095
|
-
content: [
|
|
1096
|
-
{
|
|
1097
|
-
type: "text",
|
|
1098
|
-
text: `
|
|
1099
|
-
# Post Created Successfully
|
|
1213
|
+
- Link: ${post.links.shortLink}`
|
|
1214
|
+
).join("\n\n");
|
|
1215
|
+
const location = args2.subreddit ? `r/${args2.subreddit}` : "Home Feed";
|
|
1216
|
+
return `# Top Posts from ${location} (${args2.time_filter})
|
|
1100
1217
|
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1218
|
+
${postSummaries}`;
|
|
1219
|
+
}
|
|
1220
|
+
});
|
|
1221
|
+
server.addTool({
|
|
1222
|
+
name: "get_subreddit_info",
|
|
1223
|
+
description: "Get detailed information about a subreddit including description, stats, and community analysis",
|
|
1224
|
+
parameters: import_zod.z.object({
|
|
1225
|
+
subreddit_name: import_zod.z.string().describe("The subreddit name (without r/ prefix)")
|
|
1226
|
+
}),
|
|
1227
|
+
execute: async (args2) => {
|
|
1228
|
+
const client = getRedditClient();
|
|
1229
|
+
if (!client) {
|
|
1230
|
+
throw new Error("Reddit client not initialized");
|
|
1109
1231
|
}
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
throw new import_types2.McpError(import_types2.ErrorCode.InternalError, `Failed to create post: ${String(error)}`);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
async function replyToPost(params) {
|
|
1117
|
-
const { post_id, content } = params;
|
|
1118
|
-
const client = getRedditClient();
|
|
1119
|
-
if (!client) {
|
|
1120
|
-
throw new import_types2.McpError(import_types2.ErrorCode.InternalError, "Reddit client not initialized");
|
|
1121
|
-
}
|
|
1122
|
-
try {
|
|
1123
|
-
const comment = await client.replyToPost(post_id, content);
|
|
1124
|
-
const formattedComment = formatCommentInfo(comment);
|
|
1125
|
-
return {
|
|
1126
|
-
content: [
|
|
1127
|
-
{
|
|
1128
|
-
type: "text",
|
|
1129
|
-
text: `
|
|
1130
|
-
# Reply Posted Successfully
|
|
1131
|
-
|
|
1132
|
-
## Comment Details
|
|
1133
|
-
- Author: u/${formattedComment.author}
|
|
1134
|
-
- Subreddit: r/${formattedComment.context.subreddit}
|
|
1135
|
-
- Thread: ${formattedComment.context.thread}
|
|
1136
|
-
- Link: ${formattedComment.link}
|
|
1137
|
-
|
|
1138
|
-
Your reply has been successfully posted.
|
|
1139
|
-
`
|
|
1140
|
-
}
|
|
1141
|
-
]
|
|
1142
|
-
};
|
|
1143
|
-
} catch (error) {
|
|
1144
|
-
throw new import_types2.McpError(import_types2.ErrorCode.InternalError, `Failed to reply to post: ${String(error)}`);
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
var import_types2;
|
|
1148
|
-
var init_post_tools = __esm({
|
|
1149
|
-
"src/tools/post-tools.ts"() {
|
|
1150
|
-
"use strict";
|
|
1151
|
-
init_cjs_shims();
|
|
1152
|
-
init_reddit_client();
|
|
1153
|
-
init_formatters();
|
|
1154
|
-
import_types2 = require("@modelcontextprotocol/sdk/types.js");
|
|
1155
|
-
}
|
|
1156
|
-
});
|
|
1157
|
-
|
|
1158
|
-
// src/tools/subreddit-tools.ts
|
|
1159
|
-
async function getSubredditInfo(params) {
|
|
1160
|
-
const { subreddit_name } = params;
|
|
1161
|
-
const client = getRedditClient();
|
|
1162
|
-
if (!client) {
|
|
1163
|
-
throw new import_types3.McpError(import_types3.ErrorCode.InternalError, "Reddit client not initialized");
|
|
1164
|
-
}
|
|
1165
|
-
try {
|
|
1166
|
-
const subreddit = await client.getSubredditInfo(subreddit_name);
|
|
1167
|
-
const formattedSubreddit = formatSubredditInfo(subreddit);
|
|
1168
|
-
return {
|
|
1169
|
-
content: [
|
|
1170
|
-
{
|
|
1171
|
-
type: "text",
|
|
1172
|
-
text: `
|
|
1173
|
-
# Subreddit Information: r/${formattedSubreddit.name}
|
|
1232
|
+
const subreddit = await client.getSubredditInfo(args2.subreddit_name);
|
|
1233
|
+
const formattedSubreddit = formatSubredditInfo(subreddit);
|
|
1234
|
+
return `# Subreddit Information: r/${formattedSubreddit.name}
|
|
1174
1235
|
|
|
1175
1236
|
## Overview
|
|
1176
1237
|
- Name: r/${formattedSubreddit.name}
|
|
@@ -1196,636 +1257,301 @@ ${formattedSubreddit.description.full}
|
|
|
1196
1257
|
- ${formattedSubreddit.communityAnalysis.replace(/\n - /g, "\n- ")}
|
|
1197
1258
|
|
|
1198
1259
|
## Engagement Tips
|
|
1199
|
-
- ${formattedSubreddit.engagementTips.replace(/\n - /g, "\n- ")}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
if (!client) {
|
|
1211
|
-
throw new import_types3.McpError(import_types3.ErrorCode.InternalError, "Reddit client not initialized");
|
|
1212
|
-
}
|
|
1213
|
-
try {
|
|
1214
|
-
const trendingSubreddits = await client.getTrendingSubreddits();
|
|
1215
|
-
return {
|
|
1216
|
-
content: [
|
|
1217
|
-
{
|
|
1218
|
-
type: "text",
|
|
1219
|
-
text: `
|
|
1220
|
-
# Trending Subreddits
|
|
1221
|
-
|
|
1222
|
-
${trendingSubreddits.map((subreddit, index) => `${index + 1}. r/${subreddit}`).join("\n")}
|
|
1223
|
-
`
|
|
1260
|
+
- ${formattedSubreddit.engagementTips.replace(/\n - /g, "\n- ")}`;
|
|
1261
|
+
}
|
|
1262
|
+
});
|
|
1263
|
+
server.addTool({
|
|
1264
|
+
name: "get_trending_subreddits",
|
|
1265
|
+
description: "Get a list of currently trending subreddits",
|
|
1266
|
+
parameters: import_zod.z.object({}),
|
|
1267
|
+
execute: async () => {
|
|
1268
|
+
const client = getRedditClient();
|
|
1269
|
+
if (!client) {
|
|
1270
|
+
throw new Error("Reddit client not initialized");
|
|
1224
1271
|
}
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
} catch (error) {
|
|
1228
|
-
throw new import_types3.McpError(import_types3.ErrorCode.InternalError, `Failed to fetch trending subreddits: ${String(error)}`);
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
var import_types3;
|
|
1232
|
-
var init_subreddit_tools = __esm({
|
|
1233
|
-
"src/tools/subreddit-tools.ts"() {
|
|
1234
|
-
"use strict";
|
|
1235
|
-
init_cjs_shims();
|
|
1236
|
-
init_reddit_client();
|
|
1237
|
-
init_formatters();
|
|
1238
|
-
import_types3 = require("@modelcontextprotocol/sdk/types.js");
|
|
1239
|
-
}
|
|
1240
|
-
});
|
|
1272
|
+
const trendingSubreddits = await client.getTrendingSubreddits();
|
|
1273
|
+
return `# Trending Subreddits
|
|
1241
1274
|
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
const { query, subreddit, sort = "relevance", time_filter = "all", limit = 10, type = "link" } = params;
|
|
1245
|
-
const client = getRedditClient();
|
|
1246
|
-
if (!client) {
|
|
1247
|
-
throw new import_types4.McpError(import_types4.ErrorCode.InternalError, "Reddit client not initialized");
|
|
1248
|
-
}
|
|
1249
|
-
if (!query || query.trim().length === 0) {
|
|
1250
|
-
throw new import_types4.McpError(import_types4.ErrorCode.InvalidParams, "Search query cannot be empty");
|
|
1251
|
-
}
|
|
1252
|
-
try {
|
|
1253
|
-
const posts = await client.searchReddit(query, {
|
|
1254
|
-
subreddit,
|
|
1255
|
-
sort,
|
|
1256
|
-
timeFilter: time_filter,
|
|
1257
|
-
limit,
|
|
1258
|
-
type
|
|
1275
|
+
${trendingSubreddits.map((subreddit, index) => `${index + 1}. r/${subreddit}`).join("\n")}`;
|
|
1276
|
+
}
|
|
1259
1277
|
});
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
- Author: u/${formatted.author}
|
|
1276
|
-
- Subreddit: r/${formatted.subreddit}
|
|
1277
|
-
- Score: ${formatted.score} (${formatted.upvoteRatio}% upvoted)
|
|
1278
|
-
- Comments: ${formatted.numComments}
|
|
1279
|
-
- Posted: ${formatted.createdAt}
|
|
1280
|
-
${formatted.selftext ? `
|
|
1281
|
-
${formatted.selftext.substring(0, 200)}${formatted.selftext.length > 200 ? "..." : ""}
|
|
1282
|
-
` : ""}
|
|
1283
|
-
- Link: https://reddit.com${formatted.permalink}
|
|
1284
|
-
${formatted.nsfw ? "- **NSFW**" : ""}
|
|
1285
|
-
${formatted.spoiler ? "- **Spoiler**" : ""}
|
|
1286
|
-
`;
|
|
1287
|
-
}).join("\n")}`
|
|
1278
|
+
server.addTool({
|
|
1279
|
+
name: "search_reddit",
|
|
1280
|
+
description: "Search Reddit for posts and content across subreddits",
|
|
1281
|
+
parameters: import_zod.z.object({
|
|
1282
|
+
query: import_zod.z.string().describe("Search query"),
|
|
1283
|
+
subreddit: import_zod.z.string().optional().describe("Limit search to specific subreddit (without r/ prefix)"),
|
|
1284
|
+
sort: import_zod.z.enum(["relevance", "hot", "top", "new", "comments"]).default("relevance").describe("Sort order"),
|
|
1285
|
+
time_filter: import_zod.z.enum(["hour", "day", "week", "month", "year", "all"]).default("all").describe("Time filter"),
|
|
1286
|
+
limit: import_zod.z.number().min(1).max(100).default(10).describe("Number of results"),
|
|
1287
|
+
type: import_zod.z.enum(["link", "sr", "user"]).default("link").describe("Type of content to search")
|
|
1288
|
+
}),
|
|
1289
|
+
execute: async (args2) => {
|
|
1290
|
+
const client = getRedditClient();
|
|
1291
|
+
if (!client) {
|
|
1292
|
+
throw new Error("Reddit client not initialized");
|
|
1288
1293
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1294
|
+
if (!args2.query || args2.query.trim() === "") {
|
|
1295
|
+
throw new Error("Search query cannot be empty");
|
|
1296
|
+
}
|
|
1297
|
+
const posts = await client.searchReddit(args2.query, {
|
|
1298
|
+
subreddit: args2.subreddit,
|
|
1299
|
+
sort: args2.sort,
|
|
1300
|
+
timeFilter: args2.time_filter,
|
|
1301
|
+
limit: args2.limit,
|
|
1302
|
+
type: args2.type
|
|
1303
|
+
});
|
|
1304
|
+
if (posts.length === 0) {
|
|
1305
|
+
const searchLocation2 = args2.subreddit ? ` in r/${args2.subreddit}` : "";
|
|
1306
|
+
return `No results found for "${args2.query}"${searchLocation2}.`;
|
|
1307
|
+
}
|
|
1308
|
+
const searchResults = posts.map((post, index) => {
|
|
1309
|
+
const flags = [...post.over18 ? ["**NSFW**"] : [], ...post.spoiler ? ["**Spoiler**"] : []];
|
|
1310
|
+
return `### ${index + 1}. ${post.title} ${flags.join(" ")}
|
|
1311
|
+
- Subreddit: r/${post.subreddit}
|
|
1312
|
+
- Author: u/${post.author}
|
|
1313
|
+
- Score: ${post.score.toLocaleString()} (${(post.upvoteRatio * 100).toFixed(1)}% upvoted)
|
|
1314
|
+
- Comments: ${post.numComments.toLocaleString()}
|
|
1315
|
+
- Posted: ${new Date(post.createdUtc * 1e3).toLocaleString()}
|
|
1316
|
+
- Link: https://reddit.com${post.permalink}`;
|
|
1317
|
+
}).join("\n\n");
|
|
1318
|
+
const searchLocation = args2.subreddit ? ` in r/${args2.subreddit}` : "";
|
|
1319
|
+
return `# Reddit Search Results for: "${args2.query}"${searchLocation}
|
|
1305
1320
|
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
if (!client) {
|
|
1311
|
-
throw new import_types5.McpError(import_types5.ErrorCode.InternalError, "Reddit client not initialized");
|
|
1312
|
-
}
|
|
1313
|
-
if (!post_id || !subreddit) {
|
|
1314
|
-
throw new import_types5.McpError(import_types5.ErrorCode.InvalidParams, "post_id and subreddit are required");
|
|
1315
|
-
}
|
|
1316
|
-
try {
|
|
1317
|
-
const { post, comments } = await client.getPostComments(post_id, subreddit, {
|
|
1318
|
-
sort,
|
|
1319
|
-
limit
|
|
1321
|
+
Sorted by: ${args2.sort} | Time: ${args2.time_filter} | Type: ${args2.type}
|
|
1322
|
+
|
|
1323
|
+
${searchResults}`;
|
|
1324
|
+
}
|
|
1320
1325
|
});
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1326
|
+
server.addTool({
|
|
1327
|
+
name: "create_post",
|
|
1328
|
+
description: "Create a new post in a subreddit (requires REDDIT_USERNAME and REDDIT_PASSWORD)",
|
|
1329
|
+
parameters: import_zod.z.object({
|
|
1330
|
+
subreddit: import_zod.z.string().describe("The subreddit name (without r/ prefix)"),
|
|
1331
|
+
title: import_zod.z.string().describe("The post title"),
|
|
1332
|
+
content: import_zod.z.string().describe("The post content (text for self posts, URL for link posts)"),
|
|
1333
|
+
is_self: import_zod.z.boolean().default(true).describe("Whether this is a self post (text) or link post")
|
|
1334
|
+
}),
|
|
1335
|
+
execute: async (args2) => {
|
|
1336
|
+
const client = getRedditClient();
|
|
1337
|
+
if (!client) {
|
|
1338
|
+
throw new Error("Reddit client not initialized");
|
|
1339
|
+
}
|
|
1340
|
+
if (!process.env.REDDIT_USERNAME || !process.env.REDDIT_PASSWORD) {
|
|
1341
|
+
throw new Error(
|
|
1342
|
+
"User authentication required. Please set REDDIT_USERNAME and REDDIT_PASSWORD environment variables."
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1345
|
+
const post = await client.createPost(args2.subreddit, args2.title, args2.content, args2.is_self);
|
|
1346
|
+
const formattedPost = formatPostInfo(post);
|
|
1347
|
+
return `# Post Created Successfully
|
|
1336
1348
|
|
|
1337
1349
|
## Post Details
|
|
1338
|
-
-
|
|
1350
|
+
- Title: ${formattedPost.title}
|
|
1339
1351
|
- Subreddit: r/${formattedPost.subreddit}
|
|
1340
|
-
-
|
|
1341
|
-
-
|
|
1342
|
-
- Link: https://reddit.com${formattedPost.permalink}
|
|
1352
|
+
- Type: ${formattedPost.type}
|
|
1353
|
+
- Link: ${formattedPost.links.fullPost}
|
|
1343
1354
|
|
|
1344
|
-
|
|
1345
|
-
|
|
1355
|
+
Your post has been successfully submitted to r/${formattedPost.subreddit}.`;
|
|
1356
|
+
}
|
|
1357
|
+
});
|
|
1358
|
+
server.addTool({
|
|
1359
|
+
name: "reply_to_post",
|
|
1360
|
+
description: "Post a reply to an existing Reddit post or comment (requires REDDIT_USERNAME and REDDIT_PASSWORD)",
|
|
1361
|
+
parameters: import_zod.z.object({
|
|
1362
|
+
post_id: import_zod.z.string().describe("The Reddit post ID (thing_id, e.g., t3_xxxxx for posts, t1_xxxxx for comments)"),
|
|
1363
|
+
content: import_zod.z.string().describe("The reply content")
|
|
1364
|
+
}),
|
|
1365
|
+
execute: async (args2) => {
|
|
1366
|
+
const client = getRedditClient();
|
|
1367
|
+
if (!client) {
|
|
1368
|
+
throw new Error("Reddit client not initialized");
|
|
1369
|
+
}
|
|
1370
|
+
if (!process.env.REDDIT_USERNAME || !process.env.REDDIT_PASSWORD) {
|
|
1371
|
+
throw new Error(
|
|
1372
|
+
"User authentication required. Please set REDDIT_USERNAME and REDDIT_PASSWORD environment variables."
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
const comment = await client.replyToPost(args2.post_id, args2.content);
|
|
1376
|
+
return `# Reply Posted Successfully
|
|
1346
1377
|
|
|
1347
|
-
##
|
|
1378
|
+
## Comment Details
|
|
1379
|
+
- Posted to: ${args2.post_id}
|
|
1380
|
+
- Author: u/${process.env.REDDIT_USERNAME}
|
|
1381
|
+
- Comment ID: ${comment.id}
|
|
1348
1382
|
|
|
1349
|
-
|
|
1383
|
+
Your reply has been successfully posted.`;
|
|
1384
|
+
}
|
|
1385
|
+
});
|
|
1386
|
+
server.addTool({
|
|
1387
|
+
name: "delete_post",
|
|
1388
|
+
description: "Delete your own Reddit post (requires REDDIT_USERNAME and REDDIT_PASSWORD). WARNING: This action is permanent and cannot be undone!",
|
|
1389
|
+
parameters: import_zod.z.object({
|
|
1390
|
+
thing_id: import_zod.z.string().describe(
|
|
1391
|
+
"The full Reddit thing ID (e.g., 't3_abc123' for posts) or just the post ID (e.g., 'abc123'). The 't3_' prefix will be added automatically if missing."
|
|
1392
|
+
)
|
|
1393
|
+
}),
|
|
1394
|
+
execute: async (args2) => {
|
|
1395
|
+
const client = getRedditClient();
|
|
1396
|
+
if (!client) {
|
|
1397
|
+
throw new Error("Reddit client not initialized");
|
|
1350
1398
|
}
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
var init_comment_tools = __esm({
|
|
1359
|
-
"src/tools/comment-tools.ts"() {
|
|
1360
|
-
"use strict";
|
|
1361
|
-
init_cjs_shims();
|
|
1362
|
-
init_reddit_client();
|
|
1363
|
-
init_formatters();
|
|
1364
|
-
import_types5 = require("@modelcontextprotocol/sdk/types.js");
|
|
1365
|
-
}
|
|
1366
|
-
});
|
|
1399
|
+
if (!process.env.REDDIT_USERNAME || !process.env.REDDIT_PASSWORD) {
|
|
1400
|
+
throw new Error(
|
|
1401
|
+
"User authentication required. Please set REDDIT_USERNAME and REDDIT_PASSWORD environment variables."
|
|
1402
|
+
);
|
|
1403
|
+
}
|
|
1404
|
+
await client.deletePost(args2.thing_id);
|
|
1405
|
+
return `# Post Deleted Successfully
|
|
1367
1406
|
|
|
1368
|
-
|
|
1369
|
-
var init_tools = __esm({
|
|
1370
|
-
"src/tools/index.ts"() {
|
|
1371
|
-
"use strict";
|
|
1372
|
-
init_cjs_shims();
|
|
1373
|
-
init_user_tools();
|
|
1374
|
-
init_post_tools();
|
|
1375
|
-
init_subreddit_tools();
|
|
1376
|
-
init_search_tools();
|
|
1377
|
-
init_comment_tools();
|
|
1378
|
-
}
|
|
1379
|
-
});
|
|
1407
|
+
The post ${args2.thing_id} has been permanently deleted from Reddit.
|
|
1380
1408
|
|
|
1381
|
-
|
|
1382
|
-
var src_exports = {};
|
|
1383
|
-
__export(src_exports, {
|
|
1384
|
-
RedditServer: () => RedditServer
|
|
1385
|
-
});
|
|
1386
|
-
var import_server, import_stdio, import_types6, import_dotenv, RedditServer;
|
|
1387
|
-
var init_src = __esm({
|
|
1388
|
-
"src/index.ts"() {
|
|
1389
|
-
"use strict";
|
|
1390
|
-
init_cjs_shims();
|
|
1391
|
-
import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
1392
|
-
import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
1393
|
-
import_types6 = require("@modelcontextprotocol/sdk/types.js");
|
|
1394
|
-
init_reddit_client();
|
|
1395
|
-
init_tools();
|
|
1396
|
-
import_dotenv = __toESM(require("dotenv"));
|
|
1397
|
-
import_dotenv.default.config();
|
|
1398
|
-
RedditServer = class {
|
|
1399
|
-
server;
|
|
1400
|
-
constructor() {
|
|
1401
|
-
this.server = new import_server.Server(
|
|
1402
|
-
{
|
|
1403
|
-
name: "reddit-mcp-server",
|
|
1404
|
-
version: "0.1.0"
|
|
1405
|
-
},
|
|
1406
|
-
{
|
|
1407
|
-
capabilities: {
|
|
1408
|
-
tools: {},
|
|
1409
|
-
logging: {}
|
|
1410
|
-
}
|
|
1411
|
-
}
|
|
1412
|
-
);
|
|
1413
|
-
this.initializeRedditClient();
|
|
1414
|
-
this.setupToolHandlers();
|
|
1415
|
-
this.server.onerror = async (error) => {
|
|
1416
|
-
await this.server.sendLoggingMessage({
|
|
1417
|
-
level: "error",
|
|
1418
|
-
logger: "reddit-server",
|
|
1419
|
-
data: `Server error: ${error}`
|
|
1420
|
-
});
|
|
1421
|
-
};
|
|
1422
|
-
process.on("SIGINT", async () => {
|
|
1423
|
-
await this.server.close();
|
|
1424
|
-
process.exit(0);
|
|
1425
|
-
});
|
|
1409
|
+
**Note**: This action cannot be undone. The post content has been removed and cannot be recovered.`;
|
|
1426
1410
|
}
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1411
|
+
});
|
|
1412
|
+
server.addTool({
|
|
1413
|
+
name: "delete_comment",
|
|
1414
|
+
description: "Delete your own Reddit comment (requires REDDIT_USERNAME and REDDIT_PASSWORD). WARNING: This action is permanent and cannot be undone!",
|
|
1415
|
+
parameters: import_zod.z.object({
|
|
1416
|
+
thing_id: import_zod.z.string().describe(
|
|
1417
|
+
"The full Reddit thing ID (e.g., 't1_abc123' for comments) or just the comment ID (e.g., 'abc123'). The 't1_' prefix will be added automatically if missing."
|
|
1418
|
+
)
|
|
1419
|
+
}),
|
|
1420
|
+
execute: async (args2) => {
|
|
1421
|
+
const client = getRedditClient();
|
|
1422
|
+
if (!client) {
|
|
1423
|
+
throw new Error("Reddit client not initialized");
|
|
1424
|
+
}
|
|
1425
|
+
if (!process.env.REDDIT_USERNAME || !process.env.REDDIT_PASSWORD) {
|
|
1426
|
+
throw new Error(
|
|
1427
|
+
"User authentication required. Please set REDDIT_USERNAME and REDDIT_PASSWORD environment variables."
|
|
1436
1428
|
);
|
|
1437
|
-
process.exit(1);
|
|
1438
1429
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1430
|
+
await client.deleteComment(args2.thing_id);
|
|
1431
|
+
return `# Comment Deleted Successfully
|
|
1432
|
+
|
|
1433
|
+
The comment ${args2.thing_id} has been permanently deleted from Reddit.
|
|
1434
|
+
|
|
1435
|
+
**Note**: This action cannot be undone. The comment content has been removed and cannot be recovered.`;
|
|
1436
|
+
}
|
|
1437
|
+
});
|
|
1438
|
+
server.addTool({
|
|
1439
|
+
name: "edit_post",
|
|
1440
|
+
description: "Edit your own Reddit post (self-text posts only, requires REDDIT_USERNAME and REDDIT_PASSWORD). You can only edit the text content of self posts, not titles or link posts.",
|
|
1441
|
+
parameters: import_zod.z.object({
|
|
1442
|
+
thing_id: import_zod.z.string().describe(
|
|
1443
|
+
"The full Reddit thing ID (e.g., 't3_abc123' for posts) or just the post ID (e.g., 'abc123'). The 't3_' prefix will be added automatically if missing."
|
|
1444
|
+
),
|
|
1445
|
+
new_text: import_zod.z.string().describe("The new text content for the post. Supports Reddit markdown formatting.")
|
|
1446
|
+
}),
|
|
1447
|
+
execute: async (args2) => {
|
|
1448
|
+
const client = getRedditClient();
|
|
1449
|
+
if (!client) {
|
|
1450
|
+
throw new Error("Reddit client not initialized");
|
|
1456
1451
|
}
|
|
1452
|
+
if (!process.env.REDDIT_USERNAME || !process.env.REDDIT_PASSWORD) {
|
|
1453
|
+
throw new Error(
|
|
1454
|
+
"User authentication required. Please set REDDIT_USERNAME and REDDIT_PASSWORD environment variables."
|
|
1455
|
+
);
|
|
1456
|
+
}
|
|
1457
|
+
await client.editPost(args2.thing_id, args2.new_text);
|
|
1458
|
+
return `# Post Edited Successfully
|
|
1459
|
+
|
|
1460
|
+
The post ${args2.thing_id} has been updated with your new content.
|
|
1461
|
+
|
|
1462
|
+
**Note**:
|
|
1463
|
+
- Only self (text) posts can be edited
|
|
1464
|
+
- Post titles cannot be edited
|
|
1465
|
+
- Link posts cannot be edited
|
|
1466
|
+
- An "edited" marker will appear on your post`;
|
|
1457
1467
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
}
|
|
1485
|
-
},
|
|
1486
|
-
required: ["subreddit", "post_id"]
|
|
1487
|
-
}
|
|
1488
|
-
},
|
|
1489
|
-
{
|
|
1490
|
-
name: "get_top_posts",
|
|
1491
|
-
description: "Get top posts from a subreddit",
|
|
1492
|
-
inputSchema: {
|
|
1493
|
-
type: "object",
|
|
1494
|
-
properties: {
|
|
1495
|
-
subreddit: {
|
|
1496
|
-
type: "string",
|
|
1497
|
-
description: "Name of the subreddit"
|
|
1498
|
-
},
|
|
1499
|
-
time_filter: {
|
|
1500
|
-
type: "string",
|
|
1501
|
-
description: "Time period to filter posts (e.g. 'day', 'week', 'month', 'year', 'all')",
|
|
1502
|
-
enum: ["day", "week", "month", "year", "all"],
|
|
1503
|
-
default: "week"
|
|
1504
|
-
},
|
|
1505
|
-
limit: {
|
|
1506
|
-
type: "integer",
|
|
1507
|
-
description: "Number of posts to fetch",
|
|
1508
|
-
default: 10
|
|
1509
|
-
}
|
|
1510
|
-
},
|
|
1511
|
-
required: ["subreddit"]
|
|
1512
|
-
}
|
|
1513
|
-
},
|
|
1514
|
-
{
|
|
1515
|
-
name: "get_user_info",
|
|
1516
|
-
description: "Get information about a Reddit user",
|
|
1517
|
-
inputSchema: {
|
|
1518
|
-
type: "object",
|
|
1519
|
-
properties: {
|
|
1520
|
-
username: {
|
|
1521
|
-
type: "string",
|
|
1522
|
-
description: "The username of the Reddit user to get info for"
|
|
1523
|
-
}
|
|
1524
|
-
},
|
|
1525
|
-
required: ["username"]
|
|
1526
|
-
}
|
|
1527
|
-
},
|
|
1528
|
-
{
|
|
1529
|
-
name: "get_subreddit_info",
|
|
1530
|
-
description: "Get information about a subreddit",
|
|
1531
|
-
inputSchema: {
|
|
1532
|
-
type: "object",
|
|
1533
|
-
properties: {
|
|
1534
|
-
subreddit_name: {
|
|
1535
|
-
type: "string",
|
|
1536
|
-
description: "Name of the subreddit"
|
|
1537
|
-
}
|
|
1538
|
-
},
|
|
1539
|
-
required: ["subreddit_name"]
|
|
1540
|
-
}
|
|
1541
|
-
},
|
|
1542
|
-
{
|
|
1543
|
-
name: "get_trending_subreddits",
|
|
1544
|
-
description: "Get currently trending subreddits",
|
|
1545
|
-
inputSchema: {
|
|
1546
|
-
type: "object",
|
|
1547
|
-
properties: {}
|
|
1548
|
-
}
|
|
1549
|
-
},
|
|
1550
|
-
{
|
|
1551
|
-
name: "create_post",
|
|
1552
|
-
description: "Create a new post in a subreddit",
|
|
1553
|
-
inputSchema: {
|
|
1554
|
-
type: "object",
|
|
1555
|
-
properties: {
|
|
1556
|
-
subreddit: {
|
|
1557
|
-
type: "string",
|
|
1558
|
-
description: "Name of the subreddit to post in"
|
|
1559
|
-
},
|
|
1560
|
-
title: {
|
|
1561
|
-
type: "string",
|
|
1562
|
-
description: "Title of the post"
|
|
1563
|
-
},
|
|
1564
|
-
content: {
|
|
1565
|
-
type: "string",
|
|
1566
|
-
description: "Content of the post (text for self posts, URL for link posts)"
|
|
1567
|
-
},
|
|
1568
|
-
is_self: {
|
|
1569
|
-
type: "boolean",
|
|
1570
|
-
description: "Whether this is a self (text) post (true) or link post (false)",
|
|
1571
|
-
default: true
|
|
1572
|
-
}
|
|
1573
|
-
},
|
|
1574
|
-
required: ["subreddit", "title", "content"]
|
|
1575
|
-
}
|
|
1576
|
-
},
|
|
1577
|
-
{
|
|
1578
|
-
name: "reply_to_post",
|
|
1579
|
-
description: "Post a reply to an existing Reddit post",
|
|
1580
|
-
inputSchema: {
|
|
1581
|
-
type: "object",
|
|
1582
|
-
properties: {
|
|
1583
|
-
post_id: {
|
|
1584
|
-
type: "string",
|
|
1585
|
-
description: "The ID of the post to reply to"
|
|
1586
|
-
},
|
|
1587
|
-
content: {
|
|
1588
|
-
type: "string",
|
|
1589
|
-
description: "The content of the reply"
|
|
1590
|
-
},
|
|
1591
|
-
subreddit: {
|
|
1592
|
-
type: "string",
|
|
1593
|
-
description: "The subreddit name if known (for validation)"
|
|
1594
|
-
}
|
|
1595
|
-
},
|
|
1596
|
-
required: ["post_id", "content"]
|
|
1597
|
-
}
|
|
1598
|
-
},
|
|
1599
|
-
{
|
|
1600
|
-
name: "search_reddit",
|
|
1601
|
-
description: "Search for posts on Reddit",
|
|
1602
|
-
inputSchema: {
|
|
1603
|
-
type: "object",
|
|
1604
|
-
properties: {
|
|
1605
|
-
query: {
|
|
1606
|
-
type: "string",
|
|
1607
|
-
description: "The search query"
|
|
1608
|
-
},
|
|
1609
|
-
subreddit: {
|
|
1610
|
-
type: "string",
|
|
1611
|
-
description: "Search within a specific subreddit (optional)"
|
|
1612
|
-
},
|
|
1613
|
-
sort: {
|
|
1614
|
-
type: "string",
|
|
1615
|
-
description: "Sort order: relevance, hot, top, new, comments",
|
|
1616
|
-
enum: ["relevance", "hot", "top", "new", "comments"],
|
|
1617
|
-
default: "relevance"
|
|
1618
|
-
},
|
|
1619
|
-
time_filter: {
|
|
1620
|
-
type: "string",
|
|
1621
|
-
description: "Time filter: hour, day, week, month, year, all",
|
|
1622
|
-
enum: ["hour", "day", "week", "month", "year", "all"],
|
|
1623
|
-
default: "all"
|
|
1624
|
-
},
|
|
1625
|
-
limit: {
|
|
1626
|
-
type: "number",
|
|
1627
|
-
description: "Maximum number of results to return",
|
|
1628
|
-
minimum: 1,
|
|
1629
|
-
maximum: 100,
|
|
1630
|
-
default: 10
|
|
1631
|
-
},
|
|
1632
|
-
type: {
|
|
1633
|
-
type: "string",
|
|
1634
|
-
description: "Type of content: link (posts), sr (subreddits), user (users)",
|
|
1635
|
-
enum: ["link", "sr", "user"],
|
|
1636
|
-
default: "link"
|
|
1637
|
-
}
|
|
1638
|
-
},
|
|
1639
|
-
required: ["query"]
|
|
1640
|
-
}
|
|
1641
|
-
},
|
|
1642
|
-
{
|
|
1643
|
-
name: "get_post_comments",
|
|
1644
|
-
description: "Get comments for a specific Reddit post",
|
|
1645
|
-
inputSchema: {
|
|
1646
|
-
type: "object",
|
|
1647
|
-
properties: {
|
|
1648
|
-
post_id: {
|
|
1649
|
-
type: "string",
|
|
1650
|
-
description: "The ID of the post"
|
|
1651
|
-
},
|
|
1652
|
-
subreddit: {
|
|
1653
|
-
type: "string",
|
|
1654
|
-
description: "The subreddit where the post is located"
|
|
1655
|
-
},
|
|
1656
|
-
sort: {
|
|
1657
|
-
type: "string",
|
|
1658
|
-
description: "Comment sort order: best, top, new, controversial, old, qa",
|
|
1659
|
-
enum: ["best", "top", "new", "controversial", "old", "qa"],
|
|
1660
|
-
default: "best"
|
|
1661
|
-
},
|
|
1662
|
-
limit: {
|
|
1663
|
-
type: "number",
|
|
1664
|
-
description: "Maximum number of comments to load",
|
|
1665
|
-
minimum: 1,
|
|
1666
|
-
maximum: 500,
|
|
1667
|
-
default: 100
|
|
1668
|
-
}
|
|
1669
|
-
},
|
|
1670
|
-
required: ["post_id", "subreddit"]
|
|
1671
|
-
}
|
|
1672
|
-
},
|
|
1673
|
-
{
|
|
1674
|
-
name: "get_user_posts",
|
|
1675
|
-
description: "Get posts submitted by a specific user",
|
|
1676
|
-
inputSchema: {
|
|
1677
|
-
type: "object",
|
|
1678
|
-
properties: {
|
|
1679
|
-
username: {
|
|
1680
|
-
type: "string",
|
|
1681
|
-
description: "The username to get posts for"
|
|
1682
|
-
},
|
|
1683
|
-
sort: {
|
|
1684
|
-
type: "string",
|
|
1685
|
-
description: "Sort order: new, hot, top, controversial",
|
|
1686
|
-
enum: ["new", "hot", "top", "controversial"],
|
|
1687
|
-
default: "new"
|
|
1688
|
-
},
|
|
1689
|
-
time_filter: {
|
|
1690
|
-
type: "string",
|
|
1691
|
-
description: "Time filter for top/controversial: hour, day, week, month, year, all",
|
|
1692
|
-
enum: ["hour", "day", "week", "month", "year", "all"],
|
|
1693
|
-
default: "all"
|
|
1694
|
-
},
|
|
1695
|
-
limit: {
|
|
1696
|
-
type: "number",
|
|
1697
|
-
description: "Maximum number of posts to return",
|
|
1698
|
-
minimum: 1,
|
|
1699
|
-
maximum: 100,
|
|
1700
|
-
default: 10
|
|
1701
|
-
}
|
|
1702
|
-
},
|
|
1703
|
-
required: ["username"]
|
|
1704
|
-
}
|
|
1705
|
-
},
|
|
1706
|
-
{
|
|
1707
|
-
name: "get_user_comments",
|
|
1708
|
-
description: "Get comments made by a specific user",
|
|
1709
|
-
inputSchema: {
|
|
1710
|
-
type: "object",
|
|
1711
|
-
properties: {
|
|
1712
|
-
username: {
|
|
1713
|
-
type: "string",
|
|
1714
|
-
description: "The username to get comments for"
|
|
1715
|
-
},
|
|
1716
|
-
sort: {
|
|
1717
|
-
type: "string",
|
|
1718
|
-
description: "Sort order: new, hot, top, controversial",
|
|
1719
|
-
enum: ["new", "hot", "top", "controversial"],
|
|
1720
|
-
default: "new"
|
|
1721
|
-
},
|
|
1722
|
-
time_filter: {
|
|
1723
|
-
type: "string",
|
|
1724
|
-
description: "Time filter for top/controversial: hour, day, week, month, year, all",
|
|
1725
|
-
enum: ["hour", "day", "week", "month", "year", "all"],
|
|
1726
|
-
default: "all"
|
|
1727
|
-
},
|
|
1728
|
-
limit: {
|
|
1729
|
-
type: "number",
|
|
1730
|
-
description: "Maximum number of comments to return",
|
|
1731
|
-
minimum: 1,
|
|
1732
|
-
maximum: 100,
|
|
1733
|
-
default: 10
|
|
1734
|
-
}
|
|
1735
|
-
},
|
|
1736
|
-
required: ["username"]
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
]
|
|
1740
|
-
}));
|
|
1741
|
-
this.server.setRequestHandler(import_types6.CallToolRequestSchema, async (request) => {
|
|
1742
|
-
try {
|
|
1743
|
-
const toolName = request.params.name;
|
|
1744
|
-
const toolParams = request.params.arguments || {};
|
|
1745
|
-
await this.server.sendLoggingMessage({
|
|
1746
|
-
level: "debug",
|
|
1747
|
-
logger: "reddit-server",
|
|
1748
|
-
data: `Tool call: ${toolName}`
|
|
1749
|
-
});
|
|
1750
|
-
switch (toolName) {
|
|
1751
|
-
case "test_reddit_mcp_server":
|
|
1752
|
-
return {
|
|
1753
|
-
content: [
|
|
1754
|
-
{
|
|
1755
|
-
type: "text",
|
|
1756
|
-
text: "Hello, world! The Reddit MCP Server is working correctly."
|
|
1757
|
-
}
|
|
1758
|
-
]
|
|
1759
|
-
};
|
|
1760
|
-
case "get_reddit_post":
|
|
1761
|
-
return await getRedditPost(toolParams);
|
|
1762
|
-
case "get_top_posts":
|
|
1763
|
-
return await getTopPosts(
|
|
1764
|
-
toolParams
|
|
1765
|
-
);
|
|
1766
|
-
case "get_user_info":
|
|
1767
|
-
return await getUserInfo(toolParams);
|
|
1768
|
-
case "get_subreddit_info":
|
|
1769
|
-
return await getSubredditInfo(toolParams);
|
|
1770
|
-
case "get_trending_subreddits":
|
|
1771
|
-
return await getTrendingSubreddits();
|
|
1772
|
-
case "create_post":
|
|
1773
|
-
return await createPost(
|
|
1774
|
-
toolParams
|
|
1775
|
-
);
|
|
1776
|
-
case "reply_to_post":
|
|
1777
|
-
return await replyToPost(
|
|
1778
|
-
toolParams
|
|
1779
|
-
);
|
|
1780
|
-
case "search_reddit":
|
|
1781
|
-
return await searchReddit(
|
|
1782
|
-
toolParams
|
|
1783
|
-
);
|
|
1784
|
-
case "get_post_comments":
|
|
1785
|
-
return await getPostComments(
|
|
1786
|
-
toolParams
|
|
1787
|
-
);
|
|
1788
|
-
case "get_user_posts":
|
|
1789
|
-
return await getUserPosts(
|
|
1790
|
-
toolParams
|
|
1791
|
-
);
|
|
1792
|
-
case "get_user_comments":
|
|
1793
|
-
return await getUserComments(
|
|
1794
|
-
toolParams
|
|
1795
|
-
);
|
|
1796
|
-
default:
|
|
1797
|
-
throw new import_types6.McpError(import_types6.ErrorCode.MethodNotFound, `Tool with name ${toolName} not found`);
|
|
1798
|
-
}
|
|
1799
|
-
} catch (error) {
|
|
1800
|
-
if (error instanceof Error) {
|
|
1801
|
-
await this.server.sendLoggingMessage({
|
|
1802
|
-
level: "error",
|
|
1803
|
-
logger: "reddit-server",
|
|
1804
|
-
data: `Error calling tool: ${error.message}`
|
|
1805
|
-
});
|
|
1806
|
-
throw new import_types6.McpError(import_types6.ErrorCode.InternalError, `Failed to fetch data: ${error.message}`);
|
|
1807
|
-
}
|
|
1808
|
-
throw error;
|
|
1809
|
-
}
|
|
1810
|
-
});
|
|
1468
|
+
});
|
|
1469
|
+
server.addTool({
|
|
1470
|
+
name: "edit_comment",
|
|
1471
|
+
description: "Edit your own Reddit comment (requires REDDIT_USERNAME and REDDIT_PASSWORD). Update the text content of a comment you previously posted.",
|
|
1472
|
+
parameters: import_zod.z.object({
|
|
1473
|
+
thing_id: import_zod.z.string().describe(
|
|
1474
|
+
"The full Reddit thing ID (e.g., 't1_abc123' for comments) or just the comment ID (e.g., 'abc123'). The 't1_' prefix will be added automatically if missing."
|
|
1475
|
+
),
|
|
1476
|
+
new_text: import_zod.z.string().describe("The new text content for the comment. Supports Reddit markdown formatting.")
|
|
1477
|
+
}),
|
|
1478
|
+
execute: async (args2) => {
|
|
1479
|
+
const client = getRedditClient();
|
|
1480
|
+
if (!client) {
|
|
1481
|
+
throw new Error("Reddit client not initialized");
|
|
1482
|
+
}
|
|
1483
|
+
if (!process.env.REDDIT_USERNAME || !process.env.REDDIT_PASSWORD) {
|
|
1484
|
+
throw new Error(
|
|
1485
|
+
"User authentication required. Please set REDDIT_USERNAME and REDDIT_PASSWORD environment variables."
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
await client.editComment(args2.thing_id, args2.new_text);
|
|
1489
|
+
return `# Comment Edited Successfully
|
|
1490
|
+
|
|
1491
|
+
The comment ${args2.thing_id} has been updated with your new content.
|
|
1492
|
+
|
|
1493
|
+
**Note**: An "edited" marker will appear on your comment to show it has been modified.`;
|
|
1811
1494
|
}
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1495
|
+
});
|
|
1496
|
+
server.addTool({
|
|
1497
|
+
name: "get_post_comments",
|
|
1498
|
+
description: "Get comments from a specific Reddit post",
|
|
1499
|
+
parameters: import_zod.z.object({
|
|
1500
|
+
post_id: import_zod.z.string().describe("The Reddit post ID"),
|
|
1501
|
+
subreddit: import_zod.z.string().describe("The subreddit name (without r/ prefix)"),
|
|
1502
|
+
sort: import_zod.z.enum(["best", "top", "new", "controversial", "old", "qa"]).default("best").describe("Comment sort order"),
|
|
1503
|
+
limit: import_zod.z.number().min(1).max(500).default(100).describe("Maximum number of comments to retrieve")
|
|
1504
|
+
}),
|
|
1505
|
+
execute: async (args2) => {
|
|
1506
|
+
const client = getRedditClient();
|
|
1507
|
+
if (!client) {
|
|
1508
|
+
throw new Error("Reddit client not initialized");
|
|
1509
|
+
}
|
|
1510
|
+
if (!args2.post_id || !args2.subreddit) {
|
|
1511
|
+
throw new Error("post_id and subreddit are required");
|
|
1512
|
+
}
|
|
1513
|
+
const data = await client.getPostComments(args2.post_id, args2.subreddit, {
|
|
1514
|
+
sort: args2.sort,
|
|
1515
|
+
limit: args2.limit
|
|
1826
1516
|
});
|
|
1517
|
+
const post = data.post;
|
|
1518
|
+
const comments = data.comments;
|
|
1519
|
+
let response = `# Comments for: ${post.title}
|
|
1520
|
+
|
|
1521
|
+
**Post by u/${post.author} in r/${post.subreddit}**
|
|
1522
|
+
- Score: ${post.score.toLocaleString()} | Comments: ${post.numComments.toLocaleString()}
|
|
1523
|
+
- Posted: ${new Date(post.createdUtc * 1e3).toLocaleString()}
|
|
1524
|
+
|
|
1525
|
+
---
|
|
1526
|
+
|
|
1527
|
+
`;
|
|
1528
|
+
if (comments.length === 0) {
|
|
1529
|
+
response += "No comments found for this post.";
|
|
1530
|
+
return response;
|
|
1531
|
+
}
|
|
1532
|
+
const commentSummaries = comments.map((comment) => {
|
|
1533
|
+
const indent = "\u2514\u2500".repeat(Math.min(comment.depth || 0, 3));
|
|
1534
|
+
const authorBadge = comment.isSubmitter ? " **[OP]**" : "";
|
|
1535
|
+
const editedBadge = comment.edited ? " *(edited)*" : "";
|
|
1536
|
+
return `${indent} **u/${comment.author}**${authorBadge}${editedBadge} (${comment.score.toLocaleString()} points)
|
|
1537
|
+
|
|
1538
|
+
${comment.body}
|
|
1539
|
+
|
|
1540
|
+
---`;
|
|
1541
|
+
}).join("\n\n");
|
|
1542
|
+
response += commentSummaries;
|
|
1543
|
+
return response;
|
|
1827
1544
|
}
|
|
1828
|
-
};
|
|
1545
|
+
});
|
|
1546
|
+
process.on("SIGINT", async () => {
|
|
1547
|
+
console.error("[Shutdown] Shutting down Reddit MCP Server...");
|
|
1548
|
+
process.exit(0);
|
|
1549
|
+
});
|
|
1550
|
+
process.on("SIGTERM", async () => {
|
|
1551
|
+
console.error("[Shutdown] Shutting down Reddit MCP Server...");
|
|
1552
|
+
process.exit(0);
|
|
1553
|
+
});
|
|
1554
|
+
main().catch(console.error);
|
|
1829
1555
|
}
|
|
1830
1556
|
});
|
|
1831
1557
|
|
|
@@ -1835,31 +1561,15 @@ var import_fs = __toESM(require("fs"));
|
|
|
1835
1561
|
var import_path = __toESM(require("path"));
|
|
1836
1562
|
var packageJsonPath = import_path.default.join(__dirname, "..", "package.json");
|
|
1837
1563
|
var packageJson = JSON.parse(import_fs.default.readFileSync(packageJsonPath, "utf-8"));
|
|
1564
|
+
if (!process.env.TRANSPORT_TYPE) {
|
|
1565
|
+
process.env.TRANSPORT_TYPE = "stdio";
|
|
1566
|
+
}
|
|
1838
1567
|
var args = process.argv.slice(2);
|
|
1839
1568
|
if (args.includes("--version") || args.includes("-v")) {
|
|
1840
1569
|
console.log(packageJson.version);
|
|
1841
1570
|
process.exit(0);
|
|
1842
1571
|
}
|
|
1843
|
-
|
|
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
|
-
}
|
|
1572
|
+
main2().then();
|
|
1863
1573
|
if (args.includes("--help") || args.includes("-h")) {
|
|
1864
1574
|
console.log(`
|
|
1865
1575
|
Reddit MCP Server v${packageJson.version}
|
|
@@ -1869,7 +1579,6 @@ Usage: reddit-mcp-server [options]
|
|
|
1869
1579
|
Options:
|
|
1870
1580
|
-v, --version Show version number
|
|
1871
1581
|
-h, --help Show help
|
|
1872
|
-
--generate-token Generate a secure OAuth token for HTTP server
|
|
1873
1582
|
|
|
1874
1583
|
Environment Variables:
|
|
1875
1584
|
REDDIT_CLIENT_ID Reddit API client ID (required)
|
|
@@ -1878,19 +1587,10 @@ Environment Variables:
|
|
|
1878
1587
|
REDDIT_PASSWORD Reddit password (optional, for write operations)
|
|
1879
1588
|
REDDIT_USER_AGENT Custom user agent (optional)
|
|
1880
1589
|
|
|
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
|
-
|
|
1885
1590
|
For more information, visit: https://github.com/jordanburke/reddit-mcp-server
|
|
1886
1591
|
`);
|
|
1887
1592
|
process.exit(0);
|
|
1888
1593
|
}
|
|
1889
|
-
async function
|
|
1890
|
-
|
|
1891
|
-
const server = new RedditServer2();
|
|
1892
|
-
server.run().catch((error) => {
|
|
1893
|
-
console.error(error);
|
|
1894
|
-
process.exit(1);
|
|
1895
|
-
});
|
|
1594
|
+
async function main2() {
|
|
1595
|
+
await Promise.resolve().then(() => (init_src(), src_exports));
|
|
1896
1596
|
}
|