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/index.js CHANGED
@@ -35,10 +35,9 @@ __export(src_exports, {
35
35
  module.exports = __toCommonJS(src_exports);
36
36
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
37
37
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
38
- var import_types4 = require("@modelcontextprotocol/sdk/types.js");
38
+ var import_types6 = require("@modelcontextprotocol/sdk/types.js");
39
39
 
40
40
  // src/client/reddit-client.ts
41
- var import_axios = __toESM(require("axios"));
42
41
  var RedditClient = class {
43
42
  clientId;
44
43
  clientSecret;
@@ -47,7 +46,7 @@ var RedditClient = class {
47
46
  password;
48
47
  accessToken;
49
48
  tokenExpiry = 0;
50
- api;
49
+ baseUrl = "https://oauth.reddit.com";
51
50
  authenticated = false;
52
51
  constructor(config) {
53
52
  this.clientId = config.clientId;
@@ -55,24 +54,33 @@ var RedditClient = class {
55
54
  this.userAgent = config.userAgent;
56
55
  this.username = config.username;
57
56
  this.password = config.password;
58
- this.api = import_axios.default.create({
59
- baseURL: "https://oauth.reddit.com",
60
- headers: {
61
- "User-Agent": this.userAgent
62
- }
57
+ }
58
+ async makeRequest(path, options = {}) {
59
+ if (Date.now() >= this.tokenExpiry || !this.authenticated) {
60
+ await this.authenticate();
61
+ }
62
+ const url = `${this.baseUrl}${path}`;
63
+ const headers = {
64
+ "User-Agent": this.userAgent,
65
+ Authorization: `Bearer ${this.accessToken}`,
66
+ ...options.headers
67
+ };
68
+ const response = await fetch(url, {
69
+ ...options,
70
+ headers
63
71
  });
64
- this.api.interceptors.response.use(
65
- (response) => response,
66
- async (error) => {
67
- if (error.response?.status === 401 && this.authenticated) {
68
- await this.authenticate();
69
- const originalRequest = error.config;
70
- originalRequest.headers["Authorization"] = `Bearer ${this.accessToken}`;
71
- return this.api(originalRequest);
72
- }
73
- return Promise.reject(error);
74
- }
75
- );
72
+ if (response.status === 401 && this.authenticated) {
73
+ await this.authenticate();
74
+ const retryHeaders = {
75
+ ...headers,
76
+ Authorization: `Bearer ${this.accessToken}`
77
+ };
78
+ return fetch(url, {
79
+ ...options,
80
+ headers: retryHeaders
81
+ });
82
+ }
83
+ return response;
76
84
  }
77
85
  async authenticate() {
78
86
  try {
@@ -89,20 +97,23 @@ var RedditClient = class {
89
97
  } else {
90
98
  authData.append("grant_type", "client_credentials");
91
99
  }
92
- const response = await import_axios.default.post(authUrl, authData, {
93
- auth: {
94
- username: this.clientId,
95
- password: this.clientSecret
96
- },
100
+ const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString("base64");
101
+ const response = await fetch(authUrl, {
102
+ method: "POST",
97
103
  headers: {
98
104
  "User-Agent": this.userAgent,
99
- "Content-Type": "application/x-www-form-urlencoded"
100
- }
105
+ "Content-Type": "application/x-www-form-urlencoded",
106
+ Authorization: `Basic ${credentials}`
107
+ },
108
+ body: authData.toString()
101
109
  });
102
- this.accessToken = response.data.access_token;
103
- this.tokenExpiry = now + response.data.expires_in * 1e3;
110
+ if (!response.ok) {
111
+ throw new Error(`Authentication failed: ${response.status}`);
112
+ }
113
+ const data = await response.json();
114
+ this.accessToken = data.access_token;
115
+ this.tokenExpiry = now + data.expires_in * 1e3;
104
116
  this.authenticated = true;
105
- this.api.defaults.headers.common["Authorization"] = `Bearer ${this.accessToken}`;
106
117
  } catch {
107
118
  throw new Error("Failed to authenticate with Reddit API");
108
119
  }
@@ -121,8 +132,12 @@ var RedditClient = class {
121
132
  async getUser(username) {
122
133
  await this.authenticate();
123
134
  try {
124
- const response = await this.api.get(`/user/${username}/about.json`);
125
- const data = response.data.data;
135
+ const response = await this.makeRequest(`/user/${username}/about.json`);
136
+ if (!response.ok) {
137
+ throw new Error(`HTTP ${response.status}`);
138
+ }
139
+ const json = await response.json();
140
+ const data = json.data;
126
141
  return {
127
142
  name: data.name,
128
143
  id: data.id,
@@ -142,8 +157,12 @@ var RedditClient = class {
142
157
  async getSubredditInfo(subredditName) {
143
158
  await this.authenticate();
144
159
  try {
145
- const response = await this.api.get(`/r/${subredditName}/about.json`);
146
- const data = response.data.data;
160
+ const response = await this.makeRequest(`/r/${subredditName}/about.json`);
161
+ if (!response.ok) {
162
+ throw new Error(`HTTP ${response.status}`);
163
+ }
164
+ const json = await response.json();
165
+ const data = json.data;
147
166
  return {
148
167
  displayName: data.display_name,
149
168
  title: data.title,
@@ -164,13 +183,16 @@ var RedditClient = class {
164
183
  await this.authenticate();
165
184
  try {
166
185
  const endpoint = subreddit ? `/r/${subreddit}/top.json` : "/top.json";
167
- const response = await this.api.get(endpoint, {
168
- params: {
169
- t: timeFilter,
170
- limit
171
- }
186
+ const params = new URLSearchParams({
187
+ t: timeFilter,
188
+ limit: limit.toString()
172
189
  });
173
- return response.data.data.children.map((child) => {
190
+ const response = await this.makeRequest(`${endpoint}?${params}`);
191
+ if (!response.ok) {
192
+ throw new Error(`HTTP ${response.status}`);
193
+ }
194
+ const json = await response.json();
195
+ return json.data.children.map((child) => {
174
196
  const post = child.data;
175
197
  return {
176
198
  id: post.id,
@@ -199,15 +221,19 @@ var RedditClient = class {
199
221
  await this.authenticate();
200
222
  try {
201
223
  const endpoint = subreddit ? `/r/${subreddit}/comments/${postId}.json` : `/api/info.json?id=t3_${postId}`;
202
- const response = await this.api.get(endpoint);
224
+ const response = await this.makeRequest(endpoint);
225
+ if (!response.ok) {
226
+ throw new Error(`HTTP ${response.status}`);
227
+ }
228
+ const json = await response.json();
203
229
  let post;
204
230
  if (subreddit) {
205
- post = response.data[0].data.children[0].data;
231
+ post = json[0].data.children[0].data;
206
232
  } else {
207
- if (!response.data.data.children.length) {
233
+ if (!json.data.children.length) {
208
234
  throw new Error(`Post with ID ${postId} not found`);
209
235
  }
210
- post = response.data.data.children[0].data;
236
+ post = json.data.children[0].data;
211
237
  }
212
238
  return {
213
239
  id: post.id,
@@ -234,10 +260,13 @@ var RedditClient = class {
234
260
  async getTrendingSubreddits(limit = 5) {
235
261
  await this.authenticate();
236
262
  try {
237
- const response = await this.api.get("/subreddits/popular.json", {
238
- params: { limit }
239
- });
240
- return response.data.data.children.map((child) => child.data.display_name);
263
+ const params = new URLSearchParams({ limit: limit.toString() });
264
+ const response = await this.makeRequest(`/subreddits/popular.json?${params}`);
265
+ if (!response.ok) {
266
+ throw new Error(`HTTP ${response.status}`);
267
+ }
268
+ const json = await response.json();
269
+ return json.data.children.map((child) => child.data.display_name);
241
270
  } catch {
242
271
  throw new Error("Failed to get trending subreddits");
243
272
  }
@@ -254,13 +283,19 @@ var RedditClient = class {
254
283
  params.append("kind", kind);
255
284
  params.append("title", title);
256
285
  params.append(isSelf ? "text" : "url", content);
257
- const response = await this.api.post("/api/submit", params, {
286
+ const response = await this.makeRequest("/api/submit", {
287
+ method: "POST",
258
288
  headers: {
259
289
  "Content-Type": "application/x-www-form-urlencoded"
260
- }
290
+ },
291
+ body: params.toString()
261
292
  });
262
- if (response.data.success) {
263
- const postId = response.data.data.id;
293
+ if (!response.ok) {
294
+ throw new Error(`HTTP ${response.status}`);
295
+ }
296
+ const json = await response.json();
297
+ if (json.success) {
298
+ const postId = json.data.id;
264
299
  return await this.getPost(postId);
265
300
  } else {
266
301
  throw new Error("Failed to create post");
@@ -272,8 +307,12 @@ var RedditClient = class {
272
307
  async checkPostExists(postId) {
273
308
  await this.authenticate();
274
309
  try {
275
- const response = await this.api.get(`/api/info.json?id=t3_${postId}`);
276
- return response.data.data.children.length > 0;
310
+ const response = await this.makeRequest(`/api/info.json?id=t3_${postId}`);
311
+ if (!response.ok) {
312
+ return false;
313
+ }
314
+ const json = await response.json();
315
+ return json.data.children.length > 0;
277
316
  } catch {
278
317
  return false;
279
318
  }
@@ -290,12 +329,17 @@ var RedditClient = class {
290
329
  const params = new URLSearchParams();
291
330
  params.append("thing_id", `t3_${postId}`);
292
331
  params.append("text", content);
293
- const response = await this.api.post("/api/comment", params, {
332
+ const response = await this.makeRequest("/api/comment", {
333
+ method: "POST",
294
334
  headers: {
295
335
  "Content-Type": "application/x-www-form-urlencoded"
296
- }
336
+ },
337
+ body: params.toString()
297
338
  });
298
- const commentData = response.data;
339
+ if (!response.ok) {
340
+ throw new Error(`HTTP ${response.status}`);
341
+ }
342
+ const commentData = await response.json();
299
343
  return {
300
344
  id: commentData.id,
301
345
  author: this.username,
@@ -313,6 +357,187 @@ var RedditClient = class {
313
357
  throw new Error(`Failed to reply to post ${postId}`);
314
358
  }
315
359
  }
360
+ async searchReddit(query, options = {}) {
361
+ await this.authenticate();
362
+ try {
363
+ const { subreddit, sort = "relevance", timeFilter = "all", limit = 25, type = "link" } = options;
364
+ const endpoint = subreddit ? `/r/${subreddit}/search.json` : "/search.json";
365
+ const params = new URLSearchParams({
366
+ q: query,
367
+ sort,
368
+ t: timeFilter,
369
+ limit: limit.toString(),
370
+ type,
371
+ ...subreddit && { restrict_sr: "true" }
372
+ });
373
+ const response = await this.makeRequest(`${endpoint}?${params}`);
374
+ if (!response.ok) {
375
+ throw new Error(`HTTP ${response.status}`);
376
+ }
377
+ const json = await response.json();
378
+ return json.data.children.filter((child) => child.kind === "t3").map((child) => {
379
+ const post = child.data;
380
+ return {
381
+ id: post.id,
382
+ title: post.title,
383
+ author: post.author,
384
+ subreddit: post.subreddit,
385
+ selftext: post.selftext || "",
386
+ url: post.url,
387
+ score: post.score,
388
+ upvoteRatio: post.upvote_ratio,
389
+ numComments: post.num_comments,
390
+ createdUtc: post.created_utc,
391
+ over18: post.over_18,
392
+ spoiler: post.spoiler,
393
+ edited: !!post.edited,
394
+ isSelf: post.is_self,
395
+ linkFlairText: post.link_flair_text,
396
+ permalink: post.permalink
397
+ };
398
+ });
399
+ } catch {
400
+ throw new Error(`Failed to search Reddit for: ${query}`);
401
+ }
402
+ }
403
+ async getPostComments(postId, subreddit, options = {}) {
404
+ await this.authenticate();
405
+ try {
406
+ const { sort = "best", limit = 100 } = options;
407
+ const params = new URLSearchParams({
408
+ sort,
409
+ limit: limit.toString()
410
+ });
411
+ const response = await this.makeRequest(`/r/${subreddit}/comments/${postId}.json?${params}`);
412
+ if (!response.ok) {
413
+ throw new Error(`HTTP ${response.status}`);
414
+ }
415
+ const json = await response.json();
416
+ const postData = json[0].data.children[0].data;
417
+ const post = {
418
+ id: postData.id,
419
+ title: postData.title,
420
+ author: postData.author,
421
+ subreddit: postData.subreddit,
422
+ selftext: postData.selftext || "",
423
+ url: postData.url,
424
+ score: postData.score,
425
+ upvoteRatio: postData.upvote_ratio,
426
+ numComments: postData.num_comments,
427
+ createdUtc: postData.created_utc,
428
+ over18: postData.over_18,
429
+ spoiler: postData.spoiler,
430
+ edited: !!postData.edited,
431
+ isSelf: postData.is_self,
432
+ linkFlairText: postData.link_flair_text,
433
+ permalink: postData.permalink
434
+ };
435
+ const comments = [];
436
+ const parseComments = (commentData, depth = 0) => {
437
+ for (const item of commentData) {
438
+ if (item.kind === "t1" && item.data.body) {
439
+ comments.push({
440
+ id: item.data.id,
441
+ author: item.data.author,
442
+ body: item.data.body,
443
+ score: item.data.score,
444
+ controversiality: item.data.controversiality,
445
+ subreddit: item.data.subreddit,
446
+ submissionTitle: post.title,
447
+ createdUtc: item.data.created_utc,
448
+ edited: !!item.data.edited,
449
+ isSubmitter: item.data.is_submitter,
450
+ permalink: item.data.permalink,
451
+ depth,
452
+ parentId: item.data.parent_id
453
+ });
454
+ if (item.data.replies && item.data.replies.data && item.data.replies.data.children) {
455
+ parseComments(item.data.replies.data.children, depth + 1);
456
+ }
457
+ }
458
+ }
459
+ };
460
+ if (json[1] && json[1].data && json[1].data.children) {
461
+ parseComments(json[1].data.children);
462
+ }
463
+ return { post, comments };
464
+ } catch {
465
+ throw new Error(`Failed to get comments for post ${postId}`);
466
+ }
467
+ }
468
+ async getUserPosts(username, options = {}) {
469
+ await this.authenticate();
470
+ try {
471
+ const { sort = "new", timeFilter = "all", limit = 25 } = options;
472
+ const params = new URLSearchParams({
473
+ sort,
474
+ t: timeFilter,
475
+ limit: limit.toString()
476
+ });
477
+ const response = await this.makeRequest(`/user/${username}/submitted.json?${params}`);
478
+ if (!response.ok) {
479
+ throw new Error(`HTTP ${response.status}`);
480
+ }
481
+ const json = await response.json();
482
+ return json.data.children.filter((child) => child.kind === "t3").map((child) => {
483
+ const post = child.data;
484
+ return {
485
+ id: post.id,
486
+ title: post.title,
487
+ author: post.author,
488
+ subreddit: post.subreddit,
489
+ selftext: post.selftext || "",
490
+ url: post.url,
491
+ score: post.score,
492
+ upvoteRatio: post.upvote_ratio,
493
+ numComments: post.num_comments,
494
+ createdUtc: post.created_utc,
495
+ over18: post.over_18,
496
+ spoiler: post.spoiler,
497
+ edited: !!post.edited,
498
+ isSelf: post.is_self,
499
+ linkFlairText: post.link_flair_text,
500
+ permalink: post.permalink
501
+ };
502
+ });
503
+ } catch {
504
+ throw new Error(`Failed to get posts for user ${username}`);
505
+ }
506
+ }
507
+ async getUserComments(username, options = {}) {
508
+ await this.authenticate();
509
+ try {
510
+ const { sort = "new", timeFilter = "all", limit = 25 } = options;
511
+ const params = new URLSearchParams({
512
+ sort,
513
+ t: timeFilter,
514
+ limit: limit.toString()
515
+ });
516
+ const response = await this.makeRequest(`/user/${username}/comments.json?${params}`);
517
+ if (!response.ok) {
518
+ throw new Error(`HTTP ${response.status}`);
519
+ }
520
+ const json = await response.json();
521
+ return json.data.children.filter((child) => child.kind === "t1").map((child) => {
522
+ const comment = child.data;
523
+ return {
524
+ id: comment.id,
525
+ author: comment.author,
526
+ body: comment.body,
527
+ score: comment.score,
528
+ controversiality: comment.controversiality,
529
+ subreddit: comment.subreddit,
530
+ submissionTitle: comment.link_title || "",
531
+ createdUtc: comment.created_utc,
532
+ edited: !!comment.edited,
533
+ isSubmitter: comment.is_submitter,
534
+ permalink: comment.permalink
535
+ };
536
+ });
537
+ } catch {
538
+ throw new Error(`Failed to get comments for user ${username}`);
539
+ }
540
+ }
316
541
  };
317
542
  var redditClient = null;
318
543
  function initializeRedditClient(config) {
@@ -557,6 +782,21 @@ function formatCommentInfo(comment) {
557
782
  commentAnalysis: analyzeCommentImpact(comment.score, comment.edited, comment.isSubmitter)
558
783
  };
559
784
  }
785
+ function formatPost(post) {
786
+ return {
787
+ title: post.title,
788
+ author: post.author,
789
+ subreddit: post.subreddit,
790
+ score: post.score,
791
+ upvoteRatio: Math.round(post.upvoteRatio * 100),
792
+ numComments: post.numComments,
793
+ createdAt: formatTimestamp(post.createdUtc),
794
+ selftext: post.selftext,
795
+ permalink: post.permalink,
796
+ nsfw: post.over18,
797
+ spoiler: post.spoiler
798
+ };
799
+ }
560
800
 
561
801
  // src/tools/user-tools.ts
562
802
  var import_types = require("@modelcontextprotocol/sdk/types.js");
@@ -599,6 +839,86 @@ async function getUserInfo(params) {
599
839
  throw new import_types.McpError(import_types.ErrorCode.InternalError, `Failed to fetch user data: ${String(error)}`);
600
840
  }
601
841
  }
842
+ async function getUserPosts(params) {
843
+ const { username, sort = "new", time_filter = "all", limit = 10 } = params;
844
+ const client = getRedditClient();
845
+ if (!client) {
846
+ throw new import_types.McpError(import_types.ErrorCode.InternalError, "Reddit client not initialized");
847
+ }
848
+ try {
849
+ const posts = await client.getUserPosts(username, {
850
+ sort,
851
+ timeFilter: time_filter,
852
+ limit
853
+ });
854
+ return {
855
+ content: [
856
+ {
857
+ type: "text",
858
+ text: `# Posts by u/${username}
859
+
860
+ ## Sort: ${sort} | Time: ${time_filter} | Count: ${posts.length}
861
+
862
+ ${posts.map((post, index) => {
863
+ const date = new Date(post.createdUtc * 1e3).toLocaleString();
864
+ const selftext = post.selftext ? `
865
+ ${post.selftext.substring(0, 200)}${post.selftext.length > 200 ? "..." : ""}
866
+ ` : "";
867
+ return `### ${index + 1}. ${post.title}
868
+ - Subreddit: r/${post.subreddit}
869
+ - Score: ${post.score} (${Math.round(post.upvoteRatio * 100)}% upvoted)
870
+ - Comments: ${post.numComments}
871
+ - Posted: ${date}
872
+ ${selftext}
873
+ - Link: https://reddit.com${post.permalink}
874
+ ${post.over18 ? "- **NSFW**" : ""}
875
+ ${post.spoiler ? "- **Spoiler**" : ""}`;
876
+ }).join("\n\n---\n\n")}`
877
+ }
878
+ ]
879
+ };
880
+ } catch (error) {
881
+ throw new import_types.McpError(import_types.ErrorCode.InternalError, `Failed to fetch user posts: ${String(error)}`);
882
+ }
883
+ }
884
+ async function getUserComments(params) {
885
+ const { username, sort = "new", time_filter = "all", limit = 10 } = params;
886
+ const client = getRedditClient();
887
+ if (!client) {
888
+ throw new import_types.McpError(import_types.ErrorCode.InternalError, "Reddit client not initialized");
889
+ }
890
+ try {
891
+ const comments = await client.getUserComments(username, {
892
+ sort,
893
+ timeFilter: time_filter,
894
+ limit
895
+ });
896
+ return {
897
+ content: [
898
+ {
899
+ type: "text",
900
+ text: `# Comments by u/${username}
901
+
902
+ ## Sort: ${sort} | Time: ${time_filter} | Count: ${comments.length}
903
+
904
+ ${comments.map((comment, index) => {
905
+ const date = new Date(comment.createdUtc * 1e3).toLocaleString();
906
+ const edited = comment.edited ? " *(edited)*" : "";
907
+ const body = comment.body.length > 300 ? comment.body.substring(0, 300) + "..." : comment.body;
908
+ return `### ${index + 1}. In r/${comment.subreddit} on "${comment.submissionTitle}"
909
+ - Score: ${comment.score} points
910
+ - Posted: ${date}${edited}
911
+ - Link: https://reddit.com${comment.permalink}
912
+
913
+ ${body}`;
914
+ }).join("\n\n---\n\n")}`
915
+ }
916
+ ]
917
+ };
918
+ } catch (error) {
919
+ throw new import_types.McpError(import_types.ErrorCode.InternalError, `Failed to fetch user comments: ${String(error)}`);
920
+ }
921
+ }
602
922
 
603
923
  // src/tools/post-tools.ts
604
924
  var import_types2 = require("@modelcontextprotocol/sdk/types.js");
@@ -826,6 +1146,114 @@ ${trendingSubreddits.map((subreddit, index) => `${index + 1}. r/${subreddit}`).j
826
1146
  }
827
1147
  }
828
1148
 
1149
+ // src/tools/search-tools.ts
1150
+ var import_types4 = require("@modelcontextprotocol/sdk/types.js");
1151
+ async function searchReddit(params) {
1152
+ const { query, subreddit, sort = "relevance", time_filter = "all", limit = 10, type = "link" } = params;
1153
+ const client = getRedditClient();
1154
+ if (!client) {
1155
+ throw new import_types4.McpError(import_types4.ErrorCode.InternalError, "Reddit client not initialized");
1156
+ }
1157
+ if (!query || query.trim().length === 0) {
1158
+ throw new import_types4.McpError(import_types4.ErrorCode.InvalidParams, "Search query cannot be empty");
1159
+ }
1160
+ try {
1161
+ const posts = await client.searchReddit(query, {
1162
+ subreddit,
1163
+ sort,
1164
+ timeFilter: time_filter,
1165
+ limit,
1166
+ type
1167
+ });
1168
+ return {
1169
+ content: [
1170
+ {
1171
+ type: "text",
1172
+ text: `# Reddit Search Results for: "${query}"${subreddit ? ` in r/${subreddit}` : ""}
1173
+
1174
+ ## Search Parameters
1175
+ - Sort: ${sort}
1176
+ - Time Filter: ${time_filter}
1177
+ - Type: ${type}
1178
+ - Results: ${posts.length}
1179
+
1180
+ ${posts.map((post, index) => {
1181
+ const formatted = formatPost(post);
1182
+ return `### ${index + 1}. ${formatted.title}
1183
+ - Author: u/${formatted.author}
1184
+ - Subreddit: r/${formatted.subreddit}
1185
+ - Score: ${formatted.score} (${formatted.upvoteRatio}% upvoted)
1186
+ - Comments: ${formatted.numComments}
1187
+ - Posted: ${formatted.createdAt}
1188
+ ${formatted.selftext ? `
1189
+ ${formatted.selftext.substring(0, 200)}${formatted.selftext.length > 200 ? "..." : ""}
1190
+ ` : ""}
1191
+ - Link: https://reddit.com${formatted.permalink}
1192
+ ${formatted.nsfw ? "- **NSFW**" : ""}
1193
+ ${formatted.spoiler ? "- **Spoiler**" : ""}
1194
+ `;
1195
+ }).join("\n")}`
1196
+ }
1197
+ ]
1198
+ };
1199
+ } catch (error) {
1200
+ throw new import_types4.McpError(import_types4.ErrorCode.InternalError, `Failed to search Reddit: ${String(error)}`);
1201
+ }
1202
+ }
1203
+
1204
+ // src/tools/comment-tools.ts
1205
+ var import_types5 = require("@modelcontextprotocol/sdk/types.js");
1206
+ async function getPostComments(params) {
1207
+ const { post_id, subreddit, sort = "best", limit = 100 } = params;
1208
+ const client = getRedditClient();
1209
+ if (!client) {
1210
+ throw new import_types5.McpError(import_types5.ErrorCode.InternalError, "Reddit client not initialized");
1211
+ }
1212
+ if (!post_id || !subreddit) {
1213
+ throw new import_types5.McpError(import_types5.ErrorCode.InvalidParams, "post_id and subreddit are required");
1214
+ }
1215
+ try {
1216
+ const { post, comments } = await client.getPostComments(post_id, subreddit, {
1217
+ sort,
1218
+ limit
1219
+ });
1220
+ const formattedPost = formatPost(post);
1221
+ const formatComment = (comment) => {
1222
+ const edited = comment.edited ? " *(edited)*" : "";
1223
+ const submitter = comment.isSubmitter ? " **[OP]**" : "";
1224
+ const depth = comment.depth || 0;
1225
+ const prefix = " ".repeat(depth) + (depth > 0 ? "\u2514\u2500 " : "");
1226
+ return `${prefix}**u/${comment.author}**${submitter} \u2022 ${comment.score} points \u2022 ${new Date(comment.createdUtc * 1e3).toLocaleString()}${edited}
1227
+ ${prefix}${comment.body.split("\n").join(`
1228
+ ${prefix}`)}`;
1229
+ };
1230
+ return {
1231
+ content: [
1232
+ {
1233
+ type: "text",
1234
+ text: `# Comments for: ${formattedPost.title}
1235
+
1236
+ ## Post Details
1237
+ - Author: u/${formattedPost.author}
1238
+ - Subreddit: r/${formattedPost.subreddit}
1239
+ - Score: ${formattedPost.score} (${formattedPost.upvoteRatio}% upvoted)
1240
+ - Posted: ${formattedPost.createdAt}
1241
+ - Link: https://reddit.com${formattedPost.permalink}
1242
+
1243
+ ## Post Content
1244
+ ${formattedPost.selftext || "[Link post - no text content]"}
1245
+
1246
+ ## Comments (${comments.length} loaded, sorted by ${sort})
1247
+
1248
+ ${comments.map((comment) => formatComment(comment)).join("\n\n---\n\n")}`
1249
+ }
1250
+ ]
1251
+ };
1252
+ } catch (error) {
1253
+ throw new import_types5.McpError(import_types5.ErrorCode.InternalError, `Failed to fetch comments: ${String(error)}`);
1254
+ }
1255
+ }
1256
+
829
1257
  // src/index.ts
830
1258
  var import_dotenv = __toESM(require("dotenv"));
831
1259
  import_dotenv.default.config();
@@ -864,9 +1292,6 @@ var RedditServer = class {
864
1292
  const username = process.env.REDDIT_USERNAME;
865
1293
  const password = process.env.REDDIT_PASSWORD;
866
1294
  if (!clientId || !clientSecret) {
867
- console.error(
868
- "[Error] Missing required Reddit API credentials. Please set REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET environment variables."
869
- );
870
1295
  process.exit(1);
871
1296
  }
872
1297
  try {
@@ -877,13 +1302,12 @@ var RedditServer = class {
877
1302
  username,
878
1303
  password
879
1304
  });
880
- } catch (error) {
881
- console.error("[Error] Failed to initialize Reddit client:", error);
1305
+ } catch {
882
1306
  process.exit(1);
883
1307
  }
884
1308
  }
885
1309
  setupToolHandlers() {
886
- this.server.setRequestHandler(import_types4.ListToolsRequestSchema, async () => ({
1310
+ this.server.setRequestHandler(import_types6.ListToolsRequestSchema, async () => ({
887
1311
  tools: [
888
1312
  {
889
1313
  name: "test_reddit_mcp_server",
@@ -1022,10 +1446,150 @@ var RedditServer = class {
1022
1446
  },
1023
1447
  required: ["post_id", "content"]
1024
1448
  }
1449
+ },
1450
+ {
1451
+ name: "search_reddit",
1452
+ description: "Search for posts on Reddit",
1453
+ inputSchema: {
1454
+ type: "object",
1455
+ properties: {
1456
+ query: {
1457
+ type: "string",
1458
+ description: "The search query"
1459
+ },
1460
+ subreddit: {
1461
+ type: "string",
1462
+ description: "Search within a specific subreddit (optional)"
1463
+ },
1464
+ sort: {
1465
+ type: "string",
1466
+ description: "Sort order: relevance, hot, top, new, comments",
1467
+ enum: ["relevance", "hot", "top", "new", "comments"],
1468
+ default: "relevance"
1469
+ },
1470
+ time_filter: {
1471
+ type: "string",
1472
+ description: "Time filter: hour, day, week, month, year, all",
1473
+ enum: ["hour", "day", "week", "month", "year", "all"],
1474
+ default: "all"
1475
+ },
1476
+ limit: {
1477
+ type: "number",
1478
+ description: "Maximum number of results to return",
1479
+ minimum: 1,
1480
+ maximum: 100,
1481
+ default: 10
1482
+ },
1483
+ type: {
1484
+ type: "string",
1485
+ description: "Type of content: link (posts), sr (subreddits), user (users)",
1486
+ enum: ["link", "sr", "user"],
1487
+ default: "link"
1488
+ }
1489
+ },
1490
+ required: ["query"]
1491
+ }
1492
+ },
1493
+ {
1494
+ name: "get_post_comments",
1495
+ description: "Get comments for a specific Reddit post",
1496
+ inputSchema: {
1497
+ type: "object",
1498
+ properties: {
1499
+ post_id: {
1500
+ type: "string",
1501
+ description: "The ID of the post"
1502
+ },
1503
+ subreddit: {
1504
+ type: "string",
1505
+ description: "The subreddit where the post is located"
1506
+ },
1507
+ sort: {
1508
+ type: "string",
1509
+ description: "Comment sort order: best, top, new, controversial, old, qa",
1510
+ enum: ["best", "top", "new", "controversial", "old", "qa"],
1511
+ default: "best"
1512
+ },
1513
+ limit: {
1514
+ type: "number",
1515
+ description: "Maximum number of comments to load",
1516
+ minimum: 1,
1517
+ maximum: 500,
1518
+ default: 100
1519
+ }
1520
+ },
1521
+ required: ["post_id", "subreddit"]
1522
+ }
1523
+ },
1524
+ {
1525
+ name: "get_user_posts",
1526
+ description: "Get posts submitted by a specific user",
1527
+ inputSchema: {
1528
+ type: "object",
1529
+ properties: {
1530
+ username: {
1531
+ type: "string",
1532
+ description: "The username to get posts for"
1533
+ },
1534
+ sort: {
1535
+ type: "string",
1536
+ description: "Sort order: new, hot, top, controversial",
1537
+ enum: ["new", "hot", "top", "controversial"],
1538
+ default: "new"
1539
+ },
1540
+ time_filter: {
1541
+ type: "string",
1542
+ description: "Time filter for top/controversial: hour, day, week, month, year, all",
1543
+ enum: ["hour", "day", "week", "month", "year", "all"],
1544
+ default: "all"
1545
+ },
1546
+ limit: {
1547
+ type: "number",
1548
+ description: "Maximum number of posts to return",
1549
+ minimum: 1,
1550
+ maximum: 100,
1551
+ default: 10
1552
+ }
1553
+ },
1554
+ required: ["username"]
1555
+ }
1556
+ },
1557
+ {
1558
+ name: "get_user_comments",
1559
+ description: "Get comments made by a specific user",
1560
+ inputSchema: {
1561
+ type: "object",
1562
+ properties: {
1563
+ username: {
1564
+ type: "string",
1565
+ description: "The username to get comments for"
1566
+ },
1567
+ sort: {
1568
+ type: "string",
1569
+ description: "Sort order: new, hot, top, controversial",
1570
+ enum: ["new", "hot", "top", "controversial"],
1571
+ default: "new"
1572
+ },
1573
+ time_filter: {
1574
+ type: "string",
1575
+ description: "Time filter for top/controversial: hour, day, week, month, year, all",
1576
+ enum: ["hour", "day", "week", "month", "year", "all"],
1577
+ default: "all"
1578
+ },
1579
+ limit: {
1580
+ type: "number",
1581
+ description: "Maximum number of comments to return",
1582
+ minimum: 1,
1583
+ maximum: 100,
1584
+ default: 10
1585
+ }
1586
+ },
1587
+ required: ["username"]
1588
+ }
1025
1589
  }
1026
1590
  ]
1027
1591
  }));
1028
- this.server.setRequestHandler(import_types4.CallToolRequestSchema, async (request) => {
1592
+ this.server.setRequestHandler(import_types6.CallToolRequestSchema, async (request) => {
1029
1593
  try {
1030
1594
  const toolName = request.params.name;
1031
1595
  const toolParams = request.params.arguments || {};
@@ -1064,8 +1628,24 @@ var RedditServer = class {
1064
1628
  return await replyToPost(
1065
1629
  toolParams
1066
1630
  );
1631
+ case "search_reddit":
1632
+ return await searchReddit(
1633
+ toolParams
1634
+ );
1635
+ case "get_post_comments":
1636
+ return await getPostComments(
1637
+ toolParams
1638
+ );
1639
+ case "get_user_posts":
1640
+ return await getUserPosts(
1641
+ toolParams
1642
+ );
1643
+ case "get_user_comments":
1644
+ return await getUserComments(
1645
+ toolParams
1646
+ );
1067
1647
  default:
1068
- throw new import_types4.McpError(import_types4.ErrorCode.MethodNotFound, `Tool with name ${toolName} not found`);
1648
+ throw new import_types6.McpError(import_types6.ErrorCode.MethodNotFound, `Tool with name ${toolName} not found`);
1069
1649
  }
1070
1650
  } catch (error) {
1071
1651
  if (error instanceof Error) {
@@ -1074,7 +1654,7 @@ var RedditServer = class {
1074
1654
  logger: "reddit-server",
1075
1655
  data: `Error calling tool: ${error.message}`
1076
1656
  });
1077
- throw new import_types4.McpError(import_types4.ErrorCode.InternalError, `Failed to fetch data: ${error.message}`);
1657
+ throw new import_types6.McpError(import_types6.ErrorCode.InternalError, `Failed to fetch data: ${error.message}`);
1078
1658
  }
1079
1659
  throw error;
1080
1660
  }
@@ -1099,7 +1679,9 @@ var RedditServer = class {
1099
1679
  };
1100
1680
  if (require.main === module) {
1101
1681
  const server = new RedditServer();
1102
- server.run().catch(console.error);
1682
+ server.run().catch(() => {
1683
+ process.exit(1);
1684
+ });
1103
1685
  }
1104
1686
  // Annotate the CommonJS export names for ESM import in node:
1105
1687
  0 && (module.exports = {