reddit-mcp-server 1.0.4 → 1.0.6

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/dist/bin.js CHANGED
@@ -26,10 +26,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  // src/index.ts
27
27
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
28
28
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
29
- var import_types4 = require("@modelcontextprotocol/sdk/types.js");
29
+ var import_types6 = require("@modelcontextprotocol/sdk/types.js");
30
30
 
31
31
  // src/client/reddit-client.ts
32
- var import_axios = __toESM(require("axios"));
33
32
  var RedditClient = class {
34
33
  clientId;
35
34
  clientSecret;
@@ -38,7 +37,7 @@ var RedditClient = class {
38
37
  password;
39
38
  accessToken;
40
39
  tokenExpiry = 0;
41
- api;
40
+ baseUrl = "https://oauth.reddit.com";
42
41
  authenticated = false;
43
42
  constructor(config) {
44
43
  this.clientId = config.clientId;
@@ -46,24 +45,33 @@ var RedditClient = class {
46
45
  this.userAgent = config.userAgent;
47
46
  this.username = config.username;
48
47
  this.password = config.password;
49
- this.api = import_axios.default.create({
50
- baseURL: "https://oauth.reddit.com",
51
- headers: {
52
- "User-Agent": this.userAgent
53
- }
48
+ }
49
+ async makeRequest(path, options = {}) {
50
+ if (Date.now() >= this.tokenExpiry || !this.authenticated) {
51
+ await this.authenticate();
52
+ }
53
+ const url = `${this.baseUrl}${path}`;
54
+ const headers = {
55
+ "User-Agent": this.userAgent,
56
+ Authorization: `Bearer ${this.accessToken}`,
57
+ ...options.headers
58
+ };
59
+ const response = await fetch(url, {
60
+ ...options,
61
+ headers
54
62
  });
55
- this.api.interceptors.response.use(
56
- (response) => response,
57
- async (error) => {
58
- if (error.response?.status === 401 && this.authenticated) {
59
- await this.authenticate();
60
- const originalRequest = error.config;
61
- originalRequest.headers["Authorization"] = `Bearer ${this.accessToken}`;
62
- return this.api(originalRequest);
63
- }
64
- return Promise.reject(error);
65
- }
66
- );
63
+ if (response.status === 401 && this.authenticated) {
64
+ await this.authenticate();
65
+ const retryHeaders = {
66
+ ...headers,
67
+ Authorization: `Bearer ${this.accessToken}`
68
+ };
69
+ return fetch(url, {
70
+ ...options,
71
+ headers: retryHeaders
72
+ });
73
+ }
74
+ return response;
67
75
  }
68
76
  async authenticate() {
69
77
  try {
@@ -80,20 +88,23 @@ var RedditClient = class {
80
88
  } else {
81
89
  authData.append("grant_type", "client_credentials");
82
90
  }
83
- const response = await import_axios.default.post(authUrl, authData, {
84
- auth: {
85
- username: this.clientId,
86
- password: this.clientSecret
87
- },
91
+ const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString("base64");
92
+ const response = await fetch(authUrl, {
93
+ method: "POST",
88
94
  headers: {
89
95
  "User-Agent": this.userAgent,
90
- "Content-Type": "application/x-www-form-urlencoded"
91
- }
96
+ "Content-Type": "application/x-www-form-urlencoded",
97
+ Authorization: `Basic ${credentials}`
98
+ },
99
+ body: authData.toString()
92
100
  });
93
- this.accessToken = response.data.access_token;
94
- this.tokenExpiry = now + response.data.expires_in * 1e3;
101
+ if (!response.ok) {
102
+ throw new Error(`Authentication failed: ${response.status}`);
103
+ }
104
+ const data = await response.json();
105
+ this.accessToken = data.access_token;
106
+ this.tokenExpiry = now + data.expires_in * 1e3;
95
107
  this.authenticated = true;
96
- this.api.defaults.headers.common["Authorization"] = `Bearer ${this.accessToken}`;
97
108
  } catch {
98
109
  throw new Error("Failed to authenticate with Reddit API");
99
110
  }
@@ -112,8 +123,12 @@ var RedditClient = class {
112
123
  async getUser(username) {
113
124
  await this.authenticate();
114
125
  try {
115
- const response = await this.api.get(`/user/${username}/about.json`);
116
- const data = response.data.data;
126
+ const response = await this.makeRequest(`/user/${username}/about.json`);
127
+ if (!response.ok) {
128
+ throw new Error(`HTTP ${response.status}`);
129
+ }
130
+ const json = await response.json();
131
+ const data = json.data;
117
132
  return {
118
133
  name: data.name,
119
134
  id: data.id,
@@ -133,8 +148,12 @@ var RedditClient = class {
133
148
  async getSubredditInfo(subredditName) {
134
149
  await this.authenticate();
135
150
  try {
136
- const response = await this.api.get(`/r/${subredditName}/about.json`);
137
- const data = response.data.data;
151
+ const response = await this.makeRequest(`/r/${subredditName}/about.json`);
152
+ if (!response.ok) {
153
+ throw new Error(`HTTP ${response.status}`);
154
+ }
155
+ const json = await response.json();
156
+ const data = json.data;
138
157
  return {
139
158
  displayName: data.display_name,
140
159
  title: data.title,
@@ -155,13 +174,16 @@ var RedditClient = class {
155
174
  await this.authenticate();
156
175
  try {
157
176
  const endpoint = subreddit ? `/r/${subreddit}/top.json` : "/top.json";
158
- const response = await this.api.get(endpoint, {
159
- params: {
160
- t: timeFilter,
161
- limit
162
- }
177
+ const params = new URLSearchParams({
178
+ t: timeFilter,
179
+ limit: limit.toString()
163
180
  });
164
- return response.data.data.children.map((child) => {
181
+ const response = await this.makeRequest(`${endpoint}?${params}`);
182
+ if (!response.ok) {
183
+ throw new Error(`HTTP ${response.status}`);
184
+ }
185
+ const json = await response.json();
186
+ return json.data.children.map((child) => {
165
187
  const post = child.data;
166
188
  return {
167
189
  id: post.id,
@@ -190,15 +212,19 @@ var RedditClient = class {
190
212
  await this.authenticate();
191
213
  try {
192
214
  const endpoint = subreddit ? `/r/${subreddit}/comments/${postId}.json` : `/api/info.json?id=t3_${postId}`;
193
- const response = await this.api.get(endpoint);
215
+ const response = await this.makeRequest(endpoint);
216
+ if (!response.ok) {
217
+ throw new Error(`HTTP ${response.status}`);
218
+ }
219
+ const json = await response.json();
194
220
  let post;
195
221
  if (subreddit) {
196
- post = response.data[0].data.children[0].data;
222
+ post = json[0].data.children[0].data;
197
223
  } else {
198
- if (!response.data.data.children.length) {
224
+ if (!json.data.children.length) {
199
225
  throw new Error(`Post with ID ${postId} not found`);
200
226
  }
201
- post = response.data.data.children[0].data;
227
+ post = json.data.children[0].data;
202
228
  }
203
229
  return {
204
230
  id: post.id,
@@ -225,10 +251,13 @@ var RedditClient = class {
225
251
  async getTrendingSubreddits(limit = 5) {
226
252
  await this.authenticate();
227
253
  try {
228
- const response = await this.api.get("/subreddits/popular.json", {
229
- params: { limit }
230
- });
231
- return response.data.data.children.map((child) => child.data.display_name);
254
+ const params = new URLSearchParams({ limit: limit.toString() });
255
+ const response = await this.makeRequest(`/subreddits/popular.json?${params}`);
256
+ if (!response.ok) {
257
+ throw new Error(`HTTP ${response.status}`);
258
+ }
259
+ const json = await response.json();
260
+ return json.data.children.map((child) => child.data.display_name);
232
261
  } catch {
233
262
  throw new Error("Failed to get trending subreddits");
234
263
  }
@@ -245,13 +274,19 @@ var RedditClient = class {
245
274
  params.append("kind", kind);
246
275
  params.append("title", title);
247
276
  params.append(isSelf ? "text" : "url", content);
248
- const response = await this.api.post("/api/submit", params, {
277
+ const response = await this.makeRequest("/api/submit", {
278
+ method: "POST",
249
279
  headers: {
250
280
  "Content-Type": "application/x-www-form-urlencoded"
251
- }
281
+ },
282
+ body: params.toString()
252
283
  });
253
- if (response.data.success) {
254
- const postId = response.data.data.id;
284
+ if (!response.ok) {
285
+ throw new Error(`HTTP ${response.status}`);
286
+ }
287
+ const json = await response.json();
288
+ if (json.success) {
289
+ const postId = json.data.id;
255
290
  return await this.getPost(postId);
256
291
  } else {
257
292
  throw new Error("Failed to create post");
@@ -263,8 +298,12 @@ var RedditClient = class {
263
298
  async checkPostExists(postId) {
264
299
  await this.authenticate();
265
300
  try {
266
- const response = await this.api.get(`/api/info.json?id=t3_${postId}`);
267
- return response.data.data.children.length > 0;
301
+ const response = await this.makeRequest(`/api/info.json?id=t3_${postId}`);
302
+ if (!response.ok) {
303
+ return false;
304
+ }
305
+ const json = await response.json();
306
+ return json.data.children.length > 0;
268
307
  } catch {
269
308
  return false;
270
309
  }
@@ -281,12 +320,17 @@ var RedditClient = class {
281
320
  const params = new URLSearchParams();
282
321
  params.append("thing_id", `t3_${postId}`);
283
322
  params.append("text", content);
284
- const response = await this.api.post("/api/comment", params, {
323
+ const response = await this.makeRequest("/api/comment", {
324
+ method: "POST",
285
325
  headers: {
286
326
  "Content-Type": "application/x-www-form-urlencoded"
287
- }
327
+ },
328
+ body: params.toString()
288
329
  });
289
- const commentData = response.data;
330
+ if (!response.ok) {
331
+ throw new Error(`HTTP ${response.status}`);
332
+ }
333
+ const commentData = await response.json();
290
334
  return {
291
335
  id: commentData.id,
292
336
  author: this.username,
@@ -304,6 +348,187 @@ var RedditClient = class {
304
348
  throw new Error(`Failed to reply to post ${postId}`);
305
349
  }
306
350
  }
351
+ async searchReddit(query, options = {}) {
352
+ await this.authenticate();
353
+ try {
354
+ const { subreddit, sort = "relevance", timeFilter = "all", limit = 25, type = "link" } = options;
355
+ const endpoint = subreddit ? `/r/${subreddit}/search.json` : "/search.json";
356
+ const params = new URLSearchParams({
357
+ q: query,
358
+ sort,
359
+ t: timeFilter,
360
+ limit: limit.toString(),
361
+ type,
362
+ ...subreddit && { restrict_sr: "true" }
363
+ });
364
+ const response = await this.makeRequest(`${endpoint}?${params}`);
365
+ if (!response.ok) {
366
+ throw new Error(`HTTP ${response.status}`);
367
+ }
368
+ const json = await response.json();
369
+ return json.data.children.filter((child) => child.kind === "t3").map((child) => {
370
+ const post = child.data;
371
+ return {
372
+ id: post.id,
373
+ title: post.title,
374
+ author: post.author,
375
+ subreddit: post.subreddit,
376
+ selftext: post.selftext || "",
377
+ url: post.url,
378
+ score: post.score,
379
+ upvoteRatio: post.upvote_ratio,
380
+ numComments: post.num_comments,
381
+ createdUtc: post.created_utc,
382
+ over18: post.over_18,
383
+ spoiler: post.spoiler,
384
+ edited: !!post.edited,
385
+ isSelf: post.is_self,
386
+ linkFlairText: post.link_flair_text,
387
+ permalink: post.permalink
388
+ };
389
+ });
390
+ } catch {
391
+ throw new Error(`Failed to search Reddit for: ${query}`);
392
+ }
393
+ }
394
+ async getPostComments(postId, subreddit, options = {}) {
395
+ await this.authenticate();
396
+ try {
397
+ const { sort = "best", limit = 100 } = options;
398
+ const params = new URLSearchParams({
399
+ sort,
400
+ limit: limit.toString()
401
+ });
402
+ const response = await this.makeRequest(`/r/${subreddit}/comments/${postId}.json?${params}`);
403
+ if (!response.ok) {
404
+ throw new Error(`HTTP ${response.status}`);
405
+ }
406
+ const json = await response.json();
407
+ const postData = json[0].data.children[0].data;
408
+ const post = {
409
+ id: postData.id,
410
+ title: postData.title,
411
+ author: postData.author,
412
+ subreddit: postData.subreddit,
413
+ selftext: postData.selftext || "",
414
+ url: postData.url,
415
+ score: postData.score,
416
+ upvoteRatio: postData.upvote_ratio,
417
+ numComments: postData.num_comments,
418
+ createdUtc: postData.created_utc,
419
+ over18: postData.over_18,
420
+ spoiler: postData.spoiler,
421
+ edited: !!postData.edited,
422
+ isSelf: postData.is_self,
423
+ linkFlairText: postData.link_flair_text,
424
+ permalink: postData.permalink
425
+ };
426
+ const comments = [];
427
+ const parseComments = (commentData, depth = 0) => {
428
+ for (const item of commentData) {
429
+ if (item.kind === "t1" && item.data.body) {
430
+ comments.push({
431
+ id: item.data.id,
432
+ author: item.data.author,
433
+ body: item.data.body,
434
+ score: item.data.score,
435
+ controversiality: item.data.controversiality,
436
+ subreddit: item.data.subreddit,
437
+ submissionTitle: post.title,
438
+ createdUtc: item.data.created_utc,
439
+ edited: !!item.data.edited,
440
+ isSubmitter: item.data.is_submitter,
441
+ permalink: item.data.permalink,
442
+ depth,
443
+ parentId: item.data.parent_id
444
+ });
445
+ if (item.data.replies && item.data.replies.data && item.data.replies.data.children) {
446
+ parseComments(item.data.replies.data.children, depth + 1);
447
+ }
448
+ }
449
+ }
450
+ };
451
+ if (json[1] && json[1].data && json[1].data.children) {
452
+ parseComments(json[1].data.children);
453
+ }
454
+ return { post, comments };
455
+ } catch {
456
+ throw new Error(`Failed to get comments for post ${postId}`);
457
+ }
458
+ }
459
+ async getUserPosts(username, options = {}) {
460
+ await this.authenticate();
461
+ try {
462
+ const { sort = "new", timeFilter = "all", limit = 25 } = options;
463
+ const params = new URLSearchParams({
464
+ sort,
465
+ t: timeFilter,
466
+ limit: limit.toString()
467
+ });
468
+ const response = await this.makeRequest(`/user/${username}/submitted.json?${params}`);
469
+ if (!response.ok) {
470
+ throw new Error(`HTTP ${response.status}`);
471
+ }
472
+ const json = await response.json();
473
+ return json.data.children.filter((child) => child.kind === "t3").map((child) => {
474
+ const post = child.data;
475
+ return {
476
+ id: post.id,
477
+ title: post.title,
478
+ author: post.author,
479
+ subreddit: post.subreddit,
480
+ selftext: post.selftext || "",
481
+ url: post.url,
482
+ score: post.score,
483
+ upvoteRatio: post.upvote_ratio,
484
+ numComments: post.num_comments,
485
+ createdUtc: post.created_utc,
486
+ over18: post.over_18,
487
+ spoiler: post.spoiler,
488
+ edited: !!post.edited,
489
+ isSelf: post.is_self,
490
+ linkFlairText: post.link_flair_text,
491
+ permalink: post.permalink
492
+ };
493
+ });
494
+ } catch {
495
+ throw new Error(`Failed to get posts for user ${username}`);
496
+ }
497
+ }
498
+ async getUserComments(username, options = {}) {
499
+ await this.authenticate();
500
+ try {
501
+ const { sort = "new", timeFilter = "all", limit = 25 } = options;
502
+ const params = new URLSearchParams({
503
+ sort,
504
+ t: timeFilter,
505
+ limit: limit.toString()
506
+ });
507
+ const response = await this.makeRequest(`/user/${username}/comments.json?${params}`);
508
+ if (!response.ok) {
509
+ throw new Error(`HTTP ${response.status}`);
510
+ }
511
+ const json = await response.json();
512
+ return json.data.children.filter((child) => child.kind === "t1").map((child) => {
513
+ const comment = child.data;
514
+ return {
515
+ id: comment.id,
516
+ author: comment.author,
517
+ body: comment.body,
518
+ score: comment.score,
519
+ controversiality: comment.controversiality,
520
+ subreddit: comment.subreddit,
521
+ submissionTitle: comment.link_title || "",
522
+ createdUtc: comment.created_utc,
523
+ edited: !!comment.edited,
524
+ isSubmitter: comment.is_submitter,
525
+ permalink: comment.permalink
526
+ };
527
+ });
528
+ } catch {
529
+ throw new Error(`Failed to get comments for user ${username}`);
530
+ }
531
+ }
307
532
  };
308
533
  var redditClient = null;
309
534
  function initializeRedditClient(config) {
@@ -548,6 +773,21 @@ function formatCommentInfo(comment) {
548
773
  commentAnalysis: analyzeCommentImpact(comment.score, comment.edited, comment.isSubmitter)
549
774
  };
550
775
  }
776
+ function formatPost(post) {
777
+ return {
778
+ title: post.title,
779
+ author: post.author,
780
+ subreddit: post.subreddit,
781
+ score: post.score,
782
+ upvoteRatio: Math.round(post.upvoteRatio * 100),
783
+ numComments: post.numComments,
784
+ createdAt: formatTimestamp(post.createdUtc),
785
+ selftext: post.selftext,
786
+ permalink: post.permalink,
787
+ nsfw: post.over18,
788
+ spoiler: post.spoiler
789
+ };
790
+ }
551
791
 
552
792
  // src/tools/user-tools.ts
553
793
  var import_types = require("@modelcontextprotocol/sdk/types.js");
@@ -590,6 +830,86 @@ async function getUserInfo(params) {
590
830
  throw new import_types.McpError(import_types.ErrorCode.InternalError, `Failed to fetch user data: ${String(error)}`);
591
831
  }
592
832
  }
833
+ async function getUserPosts(params) {
834
+ const { username, sort = "new", time_filter = "all", limit = 10 } = params;
835
+ const client = getRedditClient();
836
+ if (!client) {
837
+ throw new import_types.McpError(import_types.ErrorCode.InternalError, "Reddit client not initialized");
838
+ }
839
+ try {
840
+ const posts = await client.getUserPosts(username, {
841
+ sort,
842
+ timeFilter: time_filter,
843
+ limit
844
+ });
845
+ return {
846
+ content: [
847
+ {
848
+ type: "text",
849
+ text: `# Posts by u/${username}
850
+
851
+ ## Sort: ${sort} | Time: ${time_filter} | Count: ${posts.length}
852
+
853
+ ${posts.map((post, index) => {
854
+ const date = new Date(post.createdUtc * 1e3).toLocaleString();
855
+ const selftext = post.selftext ? `
856
+ ${post.selftext.substring(0, 200)}${post.selftext.length > 200 ? "..." : ""}
857
+ ` : "";
858
+ return `### ${index + 1}. ${post.title}
859
+ - Subreddit: r/${post.subreddit}
860
+ - Score: ${post.score} (${Math.round(post.upvoteRatio * 100)}% upvoted)
861
+ - Comments: ${post.numComments}
862
+ - Posted: ${date}
863
+ ${selftext}
864
+ - Link: https://reddit.com${post.permalink}
865
+ ${post.over18 ? "- **NSFW**" : ""}
866
+ ${post.spoiler ? "- **Spoiler**" : ""}`;
867
+ }).join("\n\n---\n\n")}`
868
+ }
869
+ ]
870
+ };
871
+ } catch (error) {
872
+ throw new import_types.McpError(import_types.ErrorCode.InternalError, `Failed to fetch user posts: ${String(error)}`);
873
+ }
874
+ }
875
+ async function getUserComments(params) {
876
+ const { username, sort = "new", time_filter = "all", limit = 10 } = params;
877
+ const client = getRedditClient();
878
+ if (!client) {
879
+ throw new import_types.McpError(import_types.ErrorCode.InternalError, "Reddit client not initialized");
880
+ }
881
+ try {
882
+ const comments = await client.getUserComments(username, {
883
+ sort,
884
+ timeFilter: time_filter,
885
+ limit
886
+ });
887
+ return {
888
+ content: [
889
+ {
890
+ type: "text",
891
+ text: `# Comments by u/${username}
892
+
893
+ ## Sort: ${sort} | Time: ${time_filter} | Count: ${comments.length}
894
+
895
+ ${comments.map((comment, index) => {
896
+ const date = new Date(comment.createdUtc * 1e3).toLocaleString();
897
+ const edited = comment.edited ? " *(edited)*" : "";
898
+ const body = comment.body.length > 300 ? comment.body.substring(0, 300) + "..." : comment.body;
899
+ return `### ${index + 1}. In r/${comment.subreddit} on "${comment.submissionTitle}"
900
+ - Score: ${comment.score} points
901
+ - Posted: ${date}${edited}
902
+ - Link: https://reddit.com${comment.permalink}
903
+
904
+ ${body}`;
905
+ }).join("\n\n---\n\n")}`
906
+ }
907
+ ]
908
+ };
909
+ } catch (error) {
910
+ throw new import_types.McpError(import_types.ErrorCode.InternalError, `Failed to fetch user comments: ${String(error)}`);
911
+ }
912
+ }
593
913
 
594
914
  // src/tools/post-tools.ts
595
915
  var import_types2 = require("@modelcontextprotocol/sdk/types.js");
@@ -817,6 +1137,114 @@ ${trendingSubreddits.map((subreddit, index) => `${index + 1}. r/${subreddit}`).j
817
1137
  }
818
1138
  }
819
1139
 
1140
+ // src/tools/search-tools.ts
1141
+ var import_types4 = require("@modelcontextprotocol/sdk/types.js");
1142
+ async function searchReddit(params) {
1143
+ const { query, subreddit, sort = "relevance", time_filter = "all", limit = 10, type = "link" } = params;
1144
+ const client = getRedditClient();
1145
+ if (!client) {
1146
+ throw new import_types4.McpError(import_types4.ErrorCode.InternalError, "Reddit client not initialized");
1147
+ }
1148
+ if (!query || query.trim().length === 0) {
1149
+ throw new import_types4.McpError(import_types4.ErrorCode.InvalidParams, "Search query cannot be empty");
1150
+ }
1151
+ try {
1152
+ const posts = await client.searchReddit(query, {
1153
+ subreddit,
1154
+ sort,
1155
+ timeFilter: time_filter,
1156
+ limit,
1157
+ type
1158
+ });
1159
+ return {
1160
+ content: [
1161
+ {
1162
+ type: "text",
1163
+ text: `# Reddit Search Results for: "${query}"${subreddit ? ` in r/${subreddit}` : ""}
1164
+
1165
+ ## Search Parameters
1166
+ - Sort: ${sort}
1167
+ - Time Filter: ${time_filter}
1168
+ - Type: ${type}
1169
+ - Results: ${posts.length}
1170
+
1171
+ ${posts.map((post, index) => {
1172
+ const formatted = formatPost(post);
1173
+ return `### ${index + 1}. ${formatted.title}
1174
+ - Author: u/${formatted.author}
1175
+ - Subreddit: r/${formatted.subreddit}
1176
+ - Score: ${formatted.score} (${formatted.upvoteRatio}% upvoted)
1177
+ - Comments: ${formatted.numComments}
1178
+ - Posted: ${formatted.createdAt}
1179
+ ${formatted.selftext ? `
1180
+ ${formatted.selftext.substring(0, 200)}${formatted.selftext.length > 200 ? "..." : ""}
1181
+ ` : ""}
1182
+ - Link: https://reddit.com${formatted.permalink}
1183
+ ${formatted.nsfw ? "- **NSFW**" : ""}
1184
+ ${formatted.spoiler ? "- **Spoiler**" : ""}
1185
+ `;
1186
+ }).join("\n")}`
1187
+ }
1188
+ ]
1189
+ };
1190
+ } catch (error) {
1191
+ throw new import_types4.McpError(import_types4.ErrorCode.InternalError, `Failed to search Reddit: ${String(error)}`);
1192
+ }
1193
+ }
1194
+
1195
+ // src/tools/comment-tools.ts
1196
+ var import_types5 = require("@modelcontextprotocol/sdk/types.js");
1197
+ async function getPostComments(params) {
1198
+ const { post_id, subreddit, sort = "best", limit = 100 } = params;
1199
+ const client = getRedditClient();
1200
+ if (!client) {
1201
+ throw new import_types5.McpError(import_types5.ErrorCode.InternalError, "Reddit client not initialized");
1202
+ }
1203
+ if (!post_id || !subreddit) {
1204
+ throw new import_types5.McpError(import_types5.ErrorCode.InvalidParams, "post_id and subreddit are required");
1205
+ }
1206
+ try {
1207
+ const { post, comments } = await client.getPostComments(post_id, subreddit, {
1208
+ sort,
1209
+ limit
1210
+ });
1211
+ const formattedPost = formatPost(post);
1212
+ const formatComment = (comment) => {
1213
+ const edited = comment.edited ? " *(edited)*" : "";
1214
+ const submitter = comment.isSubmitter ? " **[OP]**" : "";
1215
+ const depth = comment.depth || 0;
1216
+ const prefix = " ".repeat(depth) + (depth > 0 ? "\u2514\u2500 " : "");
1217
+ return `${prefix}**u/${comment.author}**${submitter} \u2022 ${comment.score} points \u2022 ${new Date(comment.createdUtc * 1e3).toLocaleString()}${edited}
1218
+ ${prefix}${comment.body.split("\n").join(`
1219
+ ${prefix}`)}`;
1220
+ };
1221
+ return {
1222
+ content: [
1223
+ {
1224
+ type: "text",
1225
+ text: `# Comments for: ${formattedPost.title}
1226
+
1227
+ ## Post Details
1228
+ - Author: u/${formattedPost.author}
1229
+ - Subreddit: r/${formattedPost.subreddit}
1230
+ - Score: ${formattedPost.score} (${formattedPost.upvoteRatio}% upvoted)
1231
+ - Posted: ${formattedPost.createdAt}
1232
+ - Link: https://reddit.com${formattedPost.permalink}
1233
+
1234
+ ## Post Content
1235
+ ${formattedPost.selftext || "[Link post - no text content]"}
1236
+
1237
+ ## Comments (${comments.length} loaded, sorted by ${sort})
1238
+
1239
+ ${comments.map((comment) => formatComment(comment)).join("\n\n---\n\n")}`
1240
+ }
1241
+ ]
1242
+ };
1243
+ } catch (error) {
1244
+ throw new import_types5.McpError(import_types5.ErrorCode.InternalError, `Failed to fetch comments: ${String(error)}`);
1245
+ }
1246
+ }
1247
+
820
1248
  // src/index.ts
821
1249
  var import_dotenv = __toESM(require("dotenv"));
822
1250
  import_dotenv.default.config();
@@ -855,9 +1283,6 @@ var RedditServer = class {
855
1283
  const username = process.env.REDDIT_USERNAME;
856
1284
  const password = process.env.REDDIT_PASSWORD;
857
1285
  if (!clientId || !clientSecret) {
858
- console.error(
859
- "[Error] Missing required Reddit API credentials. Please set REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET environment variables."
860
- );
861
1286
  process.exit(1);
862
1287
  }
863
1288
  try {
@@ -868,13 +1293,12 @@ var RedditServer = class {
868
1293
  username,
869
1294
  password
870
1295
  });
871
- } catch (error) {
872
- console.error("[Error] Failed to initialize Reddit client:", error);
1296
+ } catch {
873
1297
  process.exit(1);
874
1298
  }
875
1299
  }
876
1300
  setupToolHandlers() {
877
- this.server.setRequestHandler(import_types4.ListToolsRequestSchema, async () => ({
1301
+ this.server.setRequestHandler(import_types6.ListToolsRequestSchema, async () => ({
878
1302
  tools: [
879
1303
  {
880
1304
  name: "test_reddit_mcp_server",
@@ -1013,10 +1437,150 @@ var RedditServer = class {
1013
1437
  },
1014
1438
  required: ["post_id", "content"]
1015
1439
  }
1440
+ },
1441
+ {
1442
+ name: "search_reddit",
1443
+ description: "Search for posts on Reddit",
1444
+ inputSchema: {
1445
+ type: "object",
1446
+ properties: {
1447
+ query: {
1448
+ type: "string",
1449
+ description: "The search query"
1450
+ },
1451
+ subreddit: {
1452
+ type: "string",
1453
+ description: "Search within a specific subreddit (optional)"
1454
+ },
1455
+ sort: {
1456
+ type: "string",
1457
+ description: "Sort order: relevance, hot, top, new, comments",
1458
+ enum: ["relevance", "hot", "top", "new", "comments"],
1459
+ default: "relevance"
1460
+ },
1461
+ time_filter: {
1462
+ type: "string",
1463
+ description: "Time filter: hour, day, week, month, year, all",
1464
+ enum: ["hour", "day", "week", "month", "year", "all"],
1465
+ default: "all"
1466
+ },
1467
+ limit: {
1468
+ type: "number",
1469
+ description: "Maximum number of results to return",
1470
+ minimum: 1,
1471
+ maximum: 100,
1472
+ default: 10
1473
+ },
1474
+ type: {
1475
+ type: "string",
1476
+ description: "Type of content: link (posts), sr (subreddits), user (users)",
1477
+ enum: ["link", "sr", "user"],
1478
+ default: "link"
1479
+ }
1480
+ },
1481
+ required: ["query"]
1482
+ }
1483
+ },
1484
+ {
1485
+ name: "get_post_comments",
1486
+ description: "Get comments for a specific Reddit post",
1487
+ inputSchema: {
1488
+ type: "object",
1489
+ properties: {
1490
+ post_id: {
1491
+ type: "string",
1492
+ description: "The ID of the post"
1493
+ },
1494
+ subreddit: {
1495
+ type: "string",
1496
+ description: "The subreddit where the post is located"
1497
+ },
1498
+ sort: {
1499
+ type: "string",
1500
+ description: "Comment sort order: best, top, new, controversial, old, qa",
1501
+ enum: ["best", "top", "new", "controversial", "old", "qa"],
1502
+ default: "best"
1503
+ },
1504
+ limit: {
1505
+ type: "number",
1506
+ description: "Maximum number of comments to load",
1507
+ minimum: 1,
1508
+ maximum: 500,
1509
+ default: 100
1510
+ }
1511
+ },
1512
+ required: ["post_id", "subreddit"]
1513
+ }
1514
+ },
1515
+ {
1516
+ name: "get_user_posts",
1517
+ description: "Get posts submitted by a specific user",
1518
+ inputSchema: {
1519
+ type: "object",
1520
+ properties: {
1521
+ username: {
1522
+ type: "string",
1523
+ description: "The username to get posts for"
1524
+ },
1525
+ sort: {
1526
+ type: "string",
1527
+ description: "Sort order: new, hot, top, controversial",
1528
+ enum: ["new", "hot", "top", "controversial"],
1529
+ default: "new"
1530
+ },
1531
+ time_filter: {
1532
+ type: "string",
1533
+ description: "Time filter for top/controversial: hour, day, week, month, year, all",
1534
+ enum: ["hour", "day", "week", "month", "year", "all"],
1535
+ default: "all"
1536
+ },
1537
+ limit: {
1538
+ type: "number",
1539
+ description: "Maximum number of posts to return",
1540
+ minimum: 1,
1541
+ maximum: 100,
1542
+ default: 10
1543
+ }
1544
+ },
1545
+ required: ["username"]
1546
+ }
1547
+ },
1548
+ {
1549
+ name: "get_user_comments",
1550
+ description: "Get comments made by a specific user",
1551
+ inputSchema: {
1552
+ type: "object",
1553
+ properties: {
1554
+ username: {
1555
+ type: "string",
1556
+ description: "The username to get comments for"
1557
+ },
1558
+ sort: {
1559
+ type: "string",
1560
+ description: "Sort order: new, hot, top, controversial",
1561
+ enum: ["new", "hot", "top", "controversial"],
1562
+ default: "new"
1563
+ },
1564
+ time_filter: {
1565
+ type: "string",
1566
+ description: "Time filter for top/controversial: hour, day, week, month, year, all",
1567
+ enum: ["hour", "day", "week", "month", "year", "all"],
1568
+ default: "all"
1569
+ },
1570
+ limit: {
1571
+ type: "number",
1572
+ description: "Maximum number of comments to return",
1573
+ minimum: 1,
1574
+ maximum: 100,
1575
+ default: 10
1576
+ }
1577
+ },
1578
+ required: ["username"]
1579
+ }
1016
1580
  }
1017
1581
  ]
1018
1582
  }));
1019
- this.server.setRequestHandler(import_types4.CallToolRequestSchema, async (request) => {
1583
+ this.server.setRequestHandler(import_types6.CallToolRequestSchema, async (request) => {
1020
1584
  try {
1021
1585
  const toolName = request.params.name;
1022
1586
  const toolParams = request.params.arguments || {};
@@ -1055,8 +1619,24 @@ var RedditServer = class {
1055
1619
  return await replyToPost(
1056
1620
  toolParams
1057
1621
  );
1622
+ case "search_reddit":
1623
+ return await searchReddit(
1624
+ toolParams
1625
+ );
1626
+ case "get_post_comments":
1627
+ return await getPostComments(
1628
+ toolParams
1629
+ );
1630
+ case "get_user_posts":
1631
+ return await getUserPosts(
1632
+ toolParams
1633
+ );
1634
+ case "get_user_comments":
1635
+ return await getUserComments(
1636
+ toolParams
1637
+ );
1058
1638
  default:
1059
- throw new import_types4.McpError(import_types4.ErrorCode.MethodNotFound, `Tool with name ${toolName} not found`);
1639
+ throw new import_types6.McpError(import_types6.ErrorCode.MethodNotFound, `Tool with name ${toolName} not found`);
1060
1640
  }
1061
1641
  } catch (error) {
1062
1642
  if (error instanceof Error) {
@@ -1065,7 +1645,7 @@ var RedditServer = class {
1065
1645
  logger: "reddit-server",
1066
1646
  data: `Error calling tool: ${error.message}`
1067
1647
  });
1068
- throw new import_types4.McpError(import_types4.ErrorCode.InternalError, `Failed to fetch data: ${error.message}`);
1648
+ throw new import_types6.McpError(import_types6.ErrorCode.InternalError, `Failed to fetch data: ${error.message}`);
1069
1649
  }
1070
1650
  throw error;
1071
1651
  }
@@ -1090,9 +1670,13 @@ var RedditServer = class {
1090
1670
  };
1091
1671
  if (require.main === module) {
1092
1672
  const server2 = new RedditServer();
1093
- server2.run().catch(console.error);
1673
+ server2.run().catch(() => {
1674
+ process.exit(1);
1675
+ });
1094
1676
  }
1095
1677
 
1096
1678
  // src/bin.ts
1097
1679
  var server = new RedditServer();
1098
- server.run().catch(console.error);
1680
+ server.run().catch(() => {
1681
+ process.exit(1);
1682
+ });