reddit-mcp-server 1.0.7 → 1.0.9

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.
Files changed (3) hide show
  1. package/dist/bin.js +1036 -899
  2. package/dist/index.js +13 -8
  3. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -6,6 +6,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
9
16
  var __copyProps = (to, from, except, desc) => {
10
17
  if (from && typeof from === "object" || typeof from === "function") {
11
18
  for (let key of __getOwnPropNames(from))
@@ -23,521 +30,530 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
30
  mod
24
31
  ));
25
32
 
26
- // src/index.ts
27
- var import_server = require("@modelcontextprotocol/sdk/server/index.js");
28
- var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
29
- var import_types6 = require("@modelcontextprotocol/sdk/types.js");
33
+ // node_modules/.pnpm/tsup@8.5.0_postcss@8.5.6_typescript@5.8.3/node_modules/tsup/assets/cjs_shims.js
34
+ var init_cjs_shims = __esm({
35
+ "node_modules/.pnpm/tsup@8.5.0_postcss@8.5.6_typescript@5.8.3/node_modules/tsup/assets/cjs_shims.js"() {
36
+ "use strict";
37
+ }
38
+ });
30
39
 
31
40
  // src/client/reddit-client.ts
32
- var RedditClient = class {
33
- clientId;
34
- clientSecret;
35
- userAgent;
36
- username;
37
- password;
38
- accessToken;
39
- tokenExpiry = 0;
40
- baseUrl = "https://oauth.reddit.com";
41
- authenticated = false;
42
- constructor(config) {
43
- this.clientId = config.clientId;
44
- this.clientSecret = config.clientSecret;
45
- this.userAgent = config.userAgent;
46
- this.username = config.username;
47
- this.password = config.password;
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
62
- });
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;
75
- }
76
- async authenticate() {
77
- try {
78
- const now = Date.now();
79
- if (this.accessToken && now < this.tokenExpiry) {
80
- return;
81
- }
82
- const authUrl = "https://www.reddit.com/api/v1/access_token";
83
- const authData = new URLSearchParams();
84
- if (this.username && this.password) {
85
- authData.append("grant_type", "password");
86
- authData.append("username", this.username);
87
- authData.append("password", this.password);
88
- } else {
89
- authData.append("grant_type", "client_credentials");
41
+ function initializeRedditClient(config) {
42
+ redditClient = new RedditClient(config);
43
+ return redditClient;
44
+ }
45
+ function getRedditClient() {
46
+ return redditClient;
47
+ }
48
+ var RedditClient, redditClient;
49
+ var init_reddit_client = __esm({
50
+ "src/client/reddit-client.ts"() {
51
+ "use strict";
52
+ init_cjs_shims();
53
+ RedditClient = class {
54
+ clientId;
55
+ clientSecret;
56
+ userAgent;
57
+ username;
58
+ password;
59
+ accessToken;
60
+ tokenExpiry = 0;
61
+ baseUrl = "https://oauth.reddit.com";
62
+ authenticated = false;
63
+ constructor(config) {
64
+ this.clientId = config.clientId;
65
+ this.clientSecret = config.clientSecret;
66
+ this.userAgent = config.userAgent;
67
+ this.username = config.username;
68
+ this.password = config.password;
90
69
  }
91
- const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString("base64");
92
- const response = await fetch(authUrl, {
93
- method: "POST",
94
- headers: {
70
+ async makeRequest(path2, options = {}) {
71
+ if (Date.now() >= this.tokenExpiry || !this.authenticated) {
72
+ await this.authenticate();
73
+ }
74
+ const url = `${this.baseUrl}${path2}`;
75
+ const headers = {
95
76
  "User-Agent": this.userAgent,
96
- "Content-Type": "application/x-www-form-urlencoded",
97
- Authorization: `Basic ${credentials}`
98
- },
99
- body: authData.toString()
100
- });
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;
107
- this.authenticated = true;
108
- } catch {
109
- throw new Error("Failed to authenticate with Reddit API");
110
- }
111
- }
112
- async checkAuthentication() {
113
- if (!this.authenticated) {
114
- try {
115
- await this.authenticate();
116
- return true;
117
- } catch {
118
- return false;
119
- }
120
- }
121
- return true;
122
- }
123
- async getUser(username) {
124
- await this.authenticate();
125
- try {
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;
132
- return {
133
- name: data.name,
134
- id: data.id,
135
- commentKarma: data.comment_karma,
136
- linkKarma: data.link_karma,
137
- totalKarma: data.total_karma || data.comment_karma + data.link_karma,
138
- isMod: data.is_mod,
139
- isGold: data.is_gold,
140
- isEmployee: data.is_employee,
141
- createdUtc: data.created_utc,
142
- profileUrl: `https://reddit.com/user/${data.name}`
143
- };
144
- } catch {
145
- throw new Error(`Failed to get user info for ${username}`);
146
- }
147
- }
148
- async getSubredditInfo(subredditName) {
149
- await this.authenticate();
150
- try {
151
- const response = await this.makeRequest(`/r/${subredditName}/about.json`);
152
- if (!response.ok) {
153
- throw new Error(`HTTP ${response.status}`);
77
+ Authorization: `Bearer ${this.accessToken}`,
78
+ ...options.headers
79
+ };
80
+ const response = await fetch(url, {
81
+ ...options,
82
+ headers
83
+ });
84
+ if (response.status === 401 && this.authenticated) {
85
+ await this.authenticate();
86
+ const retryHeaders = {
87
+ ...headers,
88
+ Authorization: `Bearer ${this.accessToken}`
89
+ };
90
+ return fetch(url, {
91
+ ...options,
92
+ headers: retryHeaders
93
+ });
94
+ }
95
+ return response;
154
96
  }
155
- const json = await response.json();
156
- const data = json.data;
157
- return {
158
- displayName: data.display_name,
159
- title: data.title,
160
- description: data.description || "",
161
- publicDescription: data.public_description || "",
162
- subscribers: data.subscribers,
163
- activeUserCount: data.active_user_count,
164
- createdUtc: data.created_utc,
165
- over18: data.over18,
166
- subredditType: data.subreddit_type,
167
- url: data.url
168
- };
169
- } catch {
170
- throw new Error(`Failed to get subreddit info for ${subredditName}`);
171
- }
172
- }
173
- async getTopPosts(subreddit, timeFilter = "week", limit = 10) {
174
- await this.authenticate();
175
- try {
176
- const endpoint = subreddit ? `/r/${subreddit}/top.json` : "/top.json";
177
- const params = new URLSearchParams({
178
- t: timeFilter,
179
- limit: limit.toString()
180
- });
181
- const response = await this.makeRequest(`${endpoint}?${params}`);
182
- if (!response.ok) {
183
- throw new Error(`HTTP ${response.status}`);
97
+ async authenticate() {
98
+ try {
99
+ const now = Date.now();
100
+ if (this.accessToken && now < this.tokenExpiry) {
101
+ return;
102
+ }
103
+ const authUrl = "https://www.reddit.com/api/v1/access_token";
104
+ const authData = new URLSearchParams();
105
+ if (this.username && this.password) {
106
+ authData.append("grant_type", "password");
107
+ authData.append("username", this.username);
108
+ authData.append("password", this.password);
109
+ } else {
110
+ authData.append("grant_type", "client_credentials");
111
+ }
112
+ const credentials = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString("base64");
113
+ const response = await fetch(authUrl, {
114
+ method: "POST",
115
+ headers: {
116
+ "User-Agent": this.userAgent,
117
+ "Content-Type": "application/x-www-form-urlencoded",
118
+ Authorization: `Basic ${credentials}`
119
+ },
120
+ body: authData.toString()
121
+ });
122
+ if (!response.ok) {
123
+ throw new Error(`Authentication failed: ${response.status}`);
124
+ }
125
+ const data = await response.json();
126
+ this.accessToken = data.access_token;
127
+ this.tokenExpiry = now + data.expires_in * 1e3;
128
+ this.authenticated = true;
129
+ } catch {
130
+ throw new Error("Failed to authenticate with Reddit API");
131
+ }
184
132
  }
185
- const json = await response.json();
186
- return json.data.children.map((child) => {
187
- const post = child.data;
188
- return {
189
- id: post.id,
190
- title: post.title,
191
- author: post.author,
192
- subreddit: post.subreddit,
193
- selftext: post.selftext,
194
- url: post.url,
195
- score: post.score,
196
- upvoteRatio: post.upvote_ratio,
197
- numComments: post.num_comments,
198
- createdUtc: post.created_utc,
199
- over18: post.over_18,
200
- spoiler: post.spoiler,
201
- edited: !!post.edited,
202
- isSelf: post.is_self,
203
- linkFlairText: post.link_flair_text,
204
- permalink: post.permalink
205
- };
206
- });
207
- } catch {
208
- throw new Error(`Failed to get top posts for ${subreddit || "home"}`);
209
- }
210
- }
211
- async getPost(postId, subreddit) {
212
- await this.authenticate();
213
- try {
214
- const endpoint = subreddit ? `/r/${subreddit}/comments/${postId}.json` : `/api/info.json?id=t3_${postId}`;
215
- const response = await this.makeRequest(endpoint);
216
- if (!response.ok) {
217
- throw new Error(`HTTP ${response.status}`);
133
+ async checkAuthentication() {
134
+ if (!this.authenticated) {
135
+ try {
136
+ await this.authenticate();
137
+ return true;
138
+ } catch {
139
+ return false;
140
+ }
141
+ }
142
+ return true;
218
143
  }
219
- const json = await response.json();
220
- let post;
221
- if (subreddit) {
222
- post = json[0].data.children[0].data;
223
- } else {
224
- if (!json.data.children.length) {
225
- throw new Error(`Post with ID ${postId} not found`);
144
+ async getUser(username) {
145
+ await this.authenticate();
146
+ try {
147
+ const response = await this.makeRequest(`/user/${username}/about.json`);
148
+ if (!response.ok) {
149
+ throw new Error(`HTTP ${response.status}`);
150
+ }
151
+ const json = await response.json();
152
+ const data = json.data;
153
+ return {
154
+ name: data.name,
155
+ id: data.id,
156
+ commentKarma: data.comment_karma,
157
+ linkKarma: data.link_karma,
158
+ totalKarma: data.total_karma || data.comment_karma + data.link_karma,
159
+ isMod: data.is_mod,
160
+ isGold: data.is_gold,
161
+ isEmployee: data.is_employee,
162
+ createdUtc: data.created_utc,
163
+ profileUrl: `https://reddit.com/user/${data.name}`
164
+ };
165
+ } catch {
166
+ throw new Error(`Failed to get user info for ${username}`);
226
167
  }
227
- post = json.data.children[0].data;
228
168
  }
229
- return {
230
- id: post.id,
231
- title: post.title,
232
- author: post.author,
233
- subreddit: post.subreddit,
234
- selftext: post.selftext,
235
- url: post.url,
236
- score: post.score,
237
- upvoteRatio: post.upvote_ratio,
238
- numComments: post.num_comments,
239
- createdUtc: post.created_utc,
240
- over18: post.over_18,
241
- spoiler: post.spoiler,
242
- edited: !!post.edited,
243
- isSelf: post.is_self,
244
- linkFlairText: post.link_flair_text,
245
- permalink: post.permalink
246
- };
247
- } catch {
248
- throw new Error(`Failed to get post with ID ${postId}`);
249
- }
250
- }
251
- async getTrendingSubreddits(limit = 5) {
252
- await this.authenticate();
253
- try {
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}`);
169
+ async getSubredditInfo(subredditName) {
170
+ await this.authenticate();
171
+ try {
172
+ const response = await this.makeRequest(`/r/${subredditName}/about.json`);
173
+ if (!response.ok) {
174
+ throw new Error(`HTTP ${response.status}`);
175
+ }
176
+ const json = await response.json();
177
+ const data = json.data;
178
+ return {
179
+ displayName: data.display_name,
180
+ title: data.title,
181
+ description: data.description || "",
182
+ publicDescription: data.public_description || "",
183
+ subscribers: data.subscribers,
184
+ activeUserCount: data.active_user_count,
185
+ createdUtc: data.created_utc,
186
+ over18: data.over18,
187
+ subredditType: data.subreddit_type,
188
+ url: data.url
189
+ };
190
+ } catch {
191
+ throw new Error(`Failed to get subreddit info for ${subredditName}`);
192
+ }
258
193
  }
259
- const json = await response.json();
260
- return json.data.children.map((child) => child.data.display_name);
261
- } catch {
262
- throw new Error("Failed to get trending subreddits");
263
- }
264
- }
265
- async createPost(subreddit, title, content, isSelf = true) {
266
- await this.authenticate();
267
- if (!this.username || !this.password) {
268
- throw new Error("User authentication required for posting");
269
- }
270
- try {
271
- const kind = isSelf ? "self" : "link";
272
- const params = new URLSearchParams();
273
- params.append("sr", subreddit);
274
- params.append("kind", kind);
275
- params.append("title", title);
276
- params.append(isSelf ? "text" : "url", content);
277
- const response = await this.makeRequest("/api/submit", {
278
- method: "POST",
279
- headers: {
280
- "Content-Type": "application/x-www-form-urlencoded"
281
- },
282
- body: params.toString()
283
- });
284
- if (!response.ok) {
285
- throw new Error(`HTTP ${response.status}`);
194
+ async getTopPosts(subreddit, timeFilter = "week", limit = 10) {
195
+ await this.authenticate();
196
+ try {
197
+ const endpoint = subreddit ? `/r/${subreddit}/top.json` : "/top.json";
198
+ const params = new URLSearchParams({
199
+ t: timeFilter,
200
+ limit: limit.toString()
201
+ });
202
+ const response = await this.makeRequest(`${endpoint}?${params}`);
203
+ if (!response.ok) {
204
+ throw new Error(`HTTP ${response.status}`);
205
+ }
206
+ const json = await response.json();
207
+ return json.data.children.map((child) => {
208
+ const post = child.data;
209
+ return {
210
+ id: post.id,
211
+ title: post.title,
212
+ author: post.author,
213
+ subreddit: post.subreddit,
214
+ selftext: post.selftext,
215
+ url: post.url,
216
+ score: post.score,
217
+ upvoteRatio: post.upvote_ratio,
218
+ numComments: post.num_comments,
219
+ createdUtc: post.created_utc,
220
+ over18: post.over_18,
221
+ spoiler: post.spoiler,
222
+ edited: !!post.edited,
223
+ isSelf: post.is_self,
224
+ linkFlairText: post.link_flair_text,
225
+ permalink: post.permalink
226
+ };
227
+ });
228
+ } catch {
229
+ throw new Error(`Failed to get top posts for ${subreddit || "home"}`);
230
+ }
286
231
  }
287
- const json = await response.json();
288
- if (json.success) {
289
- const postId = json.data.id;
290
- return await this.getPost(postId);
291
- } else {
292
- throw new Error("Failed to create post");
232
+ async getPost(postId, subreddit) {
233
+ await this.authenticate();
234
+ try {
235
+ const endpoint = subreddit ? `/r/${subreddit}/comments/${postId}.json` : `/api/info.json?id=t3_${postId}`;
236
+ const response = await this.makeRequest(endpoint);
237
+ if (!response.ok) {
238
+ throw new Error(`HTTP ${response.status}`);
239
+ }
240
+ const json = await response.json();
241
+ let post;
242
+ if (subreddit) {
243
+ post = json[0].data.children[0].data;
244
+ } else {
245
+ if (!json.data.children.length) {
246
+ throw new Error(`Post with ID ${postId} not found`);
247
+ }
248
+ post = json.data.children[0].data;
249
+ }
250
+ return {
251
+ id: post.id,
252
+ title: post.title,
253
+ author: post.author,
254
+ subreddit: post.subreddit,
255
+ selftext: post.selftext,
256
+ url: post.url,
257
+ score: post.score,
258
+ upvoteRatio: post.upvote_ratio,
259
+ numComments: post.num_comments,
260
+ createdUtc: post.created_utc,
261
+ over18: post.over_18,
262
+ spoiler: post.spoiler,
263
+ edited: !!post.edited,
264
+ isSelf: post.is_self,
265
+ linkFlairText: post.link_flair_text,
266
+ permalink: post.permalink
267
+ };
268
+ } catch {
269
+ throw new Error(`Failed to get post with ID ${postId}`);
270
+ }
293
271
  }
294
- } catch {
295
- throw new Error(`Failed to create post in ${subreddit}`);
296
- }
297
- }
298
- async checkPostExists(postId) {
299
- await this.authenticate();
300
- try {
301
- const response = await this.makeRequest(`/api/info.json?id=t3_${postId}`);
302
- if (!response.ok) {
303
- return false;
272
+ async getTrendingSubreddits(limit = 5) {
273
+ await this.authenticate();
274
+ try {
275
+ const params = new URLSearchParams({ limit: limit.toString() });
276
+ const response = await this.makeRequest(`/subreddits/popular.json?${params}`);
277
+ if (!response.ok) {
278
+ throw new Error(`HTTP ${response.status}`);
279
+ }
280
+ const json = await response.json();
281
+ return json.data.children.map((child) => child.data.display_name);
282
+ } catch {
283
+ throw new Error("Failed to get trending subreddits");
284
+ }
304
285
  }
305
- const json = await response.json();
306
- return json.data.children.length > 0;
307
- } catch {
308
- return false;
309
- }
310
- }
311
- async replyToPost(postId, content) {
312
- await this.authenticate();
313
- if (!this.username || !this.password) {
314
- throw new Error("User authentication required for posting replies");
315
- }
316
- try {
317
- if (!await this.checkPostExists(postId)) {
318
- throw new Error(`Post with ID ${postId} does not exist or is not accessible`);
286
+ async createPost(subreddit, title, content, isSelf = true) {
287
+ await this.authenticate();
288
+ if (!this.username || !this.password) {
289
+ throw new Error("User authentication required for posting");
290
+ }
291
+ try {
292
+ const kind = isSelf ? "self" : "link";
293
+ const params = new URLSearchParams();
294
+ params.append("sr", subreddit);
295
+ params.append("kind", kind);
296
+ params.append("title", title);
297
+ params.append(isSelf ? "text" : "url", content);
298
+ const response = await this.makeRequest("/api/submit", {
299
+ method: "POST",
300
+ headers: {
301
+ "Content-Type": "application/x-www-form-urlencoded"
302
+ },
303
+ body: params.toString()
304
+ });
305
+ if (!response.ok) {
306
+ throw new Error(`HTTP ${response.status}`);
307
+ }
308
+ const json = await response.json();
309
+ if (json.success) {
310
+ const postId = json.data.id;
311
+ return await this.getPost(postId);
312
+ } else {
313
+ throw new Error("Failed to create post");
314
+ }
315
+ } catch {
316
+ throw new Error(`Failed to create post in ${subreddit}`);
317
+ }
319
318
  }
320
- const params = new URLSearchParams();
321
- params.append("thing_id", `t3_${postId}`);
322
- params.append("text", content);
323
- const response = await this.makeRequest("/api/comment", {
324
- method: "POST",
325
- headers: {
326
- "Content-Type": "application/x-www-form-urlencoded"
327
- },
328
- body: params.toString()
329
- });
330
- if (!response.ok) {
331
- throw new Error(`HTTP ${response.status}`);
319
+ async checkPostExists(postId) {
320
+ await this.authenticate();
321
+ try {
322
+ const response = await this.makeRequest(`/api/info.json?id=t3_${postId}`);
323
+ if (!response.ok) {
324
+ return false;
325
+ }
326
+ const json = await response.json();
327
+ return json.data.children.length > 0;
328
+ } catch {
329
+ return false;
330
+ }
332
331
  }
333
- const commentData = await response.json();
334
- return {
335
- id: commentData.id,
336
- author: this.username,
337
- body: content,
338
- score: 1,
339
- controversiality: 0,
340
- subreddit: commentData.subreddit,
341
- submissionTitle: commentData.link_title,
342
- createdUtc: Date.now() / 1e3,
343
- edited: false,
344
- isSubmitter: false,
345
- permalink: commentData.permalink
346
- };
347
- } catch {
348
- throw new Error(`Failed to reply to post ${postId}`);
349
- }
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}`);
332
+ async replyToPost(postId, content) {
333
+ await this.authenticate();
334
+ if (!this.username || !this.password) {
335
+ throw new Error("User authentication required for posting replies");
336
+ }
337
+ try {
338
+ if (!await this.checkPostExists(postId)) {
339
+ throw new Error(`Post with ID ${postId} does not exist or is not accessible`);
340
+ }
341
+ const params = new URLSearchParams();
342
+ params.append("thing_id", `t3_${postId}`);
343
+ params.append("text", content);
344
+ const response = await this.makeRequest("/api/comment", {
345
+ method: "POST",
346
+ headers: {
347
+ "Content-Type": "application/x-www-form-urlencoded"
348
+ },
349
+ body: params.toString()
350
+ });
351
+ if (!response.ok) {
352
+ throw new Error(`HTTP ${response.status}`);
353
+ }
354
+ const commentData = await response.json();
355
+ return {
356
+ id: commentData.id,
357
+ author: this.username,
358
+ body: content,
359
+ score: 1,
360
+ controversiality: 0,
361
+ subreddit: commentData.subreddit,
362
+ submissionTitle: commentData.link_title,
363
+ createdUtc: Date.now() / 1e3,
364
+ edited: false,
365
+ isSubmitter: false,
366
+ permalink: commentData.permalink
367
+ };
368
+ } catch {
369
+ throw new Error(`Failed to reply to post ${postId}`);
370
+ }
367
371
  }
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}`);
372
+ async searchReddit(query, options = {}) {
373
+ await this.authenticate();
374
+ try {
375
+ const { subreddit, sort = "relevance", timeFilter = "all", limit = 25, type = "link" } = options;
376
+ const endpoint = subreddit ? `/r/${subreddit}/search.json` : "/search.json";
377
+ const params = new URLSearchParams({
378
+ q: query,
379
+ sort,
380
+ t: timeFilter,
381
+ limit: limit.toString(),
382
+ type,
383
+ ...subreddit && { restrict_sr: "true" }
384
+ });
385
+ const response = await this.makeRequest(`${endpoint}?${params}`);
386
+ if (!response.ok) {
387
+ throw new Error(`HTTP ${response.status}`);
388
+ }
389
+ const json = await response.json();
390
+ return json.data.children.filter((child) => child.kind === "t3").map((child) => {
391
+ const post = child.data;
392
+ return {
393
+ id: post.id,
394
+ title: post.title,
395
+ author: post.author,
396
+ subreddit: post.subreddit,
397
+ selftext: post.selftext || "",
398
+ url: post.url,
399
+ score: post.score,
400
+ upvoteRatio: post.upvote_ratio,
401
+ numComments: post.num_comments,
402
+ createdUtc: post.created_utc,
403
+ over18: post.over_18,
404
+ spoiler: post.spoiler,
405
+ edited: !!post.edited,
406
+ isSelf: post.is_self,
407
+ linkFlairText: post.link_flair_text,
408
+ permalink: post.permalink
409
+ };
410
+ });
411
+ } catch {
412
+ throw new Error(`Failed to search Reddit for: ${query}`);
413
+ }
405
414
  }
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);
415
+ async getPostComments(postId, subreddit, options = {}) {
416
+ await this.authenticate();
417
+ try {
418
+ const { sort = "best", limit = 100 } = options;
419
+ const params = new URLSearchParams({
420
+ sort,
421
+ limit: limit.toString()
422
+ });
423
+ const response = await this.makeRequest(`/r/${subreddit}/comments/${postId}.json?${params}`);
424
+ if (!response.ok) {
425
+ throw new Error(`HTTP ${response.status}`);
426
+ }
427
+ const json = await response.json();
428
+ const postData = json[0].data.children[0].data;
429
+ const post = {
430
+ id: postData.id,
431
+ title: postData.title,
432
+ author: postData.author,
433
+ subreddit: postData.subreddit,
434
+ selftext: postData.selftext || "",
435
+ url: postData.url,
436
+ score: postData.score,
437
+ upvoteRatio: postData.upvote_ratio,
438
+ numComments: postData.num_comments,
439
+ createdUtc: postData.created_utc,
440
+ over18: postData.over_18,
441
+ spoiler: postData.spoiler,
442
+ edited: !!postData.edited,
443
+ isSelf: postData.is_self,
444
+ linkFlairText: postData.link_flair_text,
445
+ permalink: postData.permalink
446
+ };
447
+ const comments = [];
448
+ const parseComments = (commentData, depth = 0) => {
449
+ for (const item of commentData) {
450
+ if (item.kind === "t1" && item.data.body) {
451
+ comments.push({
452
+ id: item.data.id,
453
+ author: item.data.author,
454
+ body: item.data.body,
455
+ score: item.data.score,
456
+ controversiality: item.data.controversiality,
457
+ subreddit: item.data.subreddit,
458
+ submissionTitle: post.title,
459
+ createdUtc: item.data.created_utc,
460
+ edited: !!item.data.edited,
461
+ isSubmitter: item.data.is_submitter,
462
+ permalink: item.data.permalink,
463
+ depth,
464
+ parentId: item.data.parent_id
465
+ });
466
+ if (item.data.replies && item.data.replies.data && item.data.replies.data.children) {
467
+ parseComments(item.data.replies.data.children, depth + 1);
468
+ }
469
+ }
447
470
  }
471
+ };
472
+ if (json[1] && json[1].data && json[1].data.children) {
473
+ parseComments(json[1].data.children);
448
474
  }
475
+ return { post, comments };
476
+ } catch {
477
+ throw new Error(`Failed to get comments for post ${postId}`);
449
478
  }
450
- };
451
- if (json[1] && json[1].data && json[1].data.children) {
452
- parseComments(json[1].data.children);
453
479
  }
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}`);
480
+ async getUserPosts(username, options = {}) {
481
+ await this.authenticate();
482
+ try {
483
+ const { sort = "new", timeFilter = "all", limit = 25 } = options;
484
+ const params = new URLSearchParams({
485
+ sort,
486
+ t: timeFilter,
487
+ limit: limit.toString()
488
+ });
489
+ const response = await this.makeRequest(`/user/${username}/submitted.json?${params}`);
490
+ if (!response.ok) {
491
+ throw new Error(`HTTP ${response.status}`);
492
+ }
493
+ const json = await response.json();
494
+ return json.data.children.filter((child) => child.kind === "t3").map((child) => {
495
+ const post = child.data;
496
+ return {
497
+ id: post.id,
498
+ title: post.title,
499
+ author: post.author,
500
+ subreddit: post.subreddit,
501
+ selftext: post.selftext || "",
502
+ url: post.url,
503
+ score: post.score,
504
+ upvoteRatio: post.upvote_ratio,
505
+ numComments: post.num_comments,
506
+ createdUtc: post.created_utc,
507
+ over18: post.over_18,
508
+ spoiler: post.spoiler,
509
+ edited: !!post.edited,
510
+ isSelf: post.is_self,
511
+ linkFlairText: post.link_flair_text,
512
+ permalink: post.permalink
513
+ };
514
+ });
515
+ } catch {
516
+ throw new Error(`Failed to get posts for user ${username}`);
517
+ }
471
518
  }
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}`);
519
+ async getUserComments(username, options = {}) {
520
+ await this.authenticate();
521
+ try {
522
+ const { sort = "new", timeFilter = "all", limit = 25 } = options;
523
+ const params = new URLSearchParams({
524
+ sort,
525
+ t: timeFilter,
526
+ limit: limit.toString()
527
+ });
528
+ const response = await this.makeRequest(`/user/${username}/comments.json?${params}`);
529
+ if (!response.ok) {
530
+ throw new Error(`HTTP ${response.status}`);
531
+ }
532
+ const json = await response.json();
533
+ return json.data.children.filter((child) => child.kind === "t1").map((child) => {
534
+ const comment = child.data;
535
+ return {
536
+ id: comment.id,
537
+ author: comment.author,
538
+ body: comment.body,
539
+ score: comment.score,
540
+ controversiality: comment.controversiality,
541
+ subreddit: comment.subreddit,
542
+ submissionTitle: comment.link_title || "",
543
+ createdUtc: comment.created_utc,
544
+ edited: !!comment.edited,
545
+ isSubmitter: comment.is_submitter,
546
+ permalink: comment.permalink
547
+ };
548
+ });
549
+ } catch {
550
+ throw new Error(`Failed to get comments for user ${username}`);
551
+ }
510
552
  }
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
- }
553
+ };
554
+ redditClient = null;
531
555
  }
532
- };
533
- var redditClient = null;
534
- function initializeRedditClient(config) {
535
- redditClient = new RedditClient(config);
536
- return redditClient;
537
- }
538
- function getRedditClient() {
539
- return redditClient;
540
- }
556
+ });
541
557
 
542
558
  // src/utils/formatters.ts
543
559
  function formatTimestamp(timestamp) {
@@ -788,9 +804,14 @@ function formatPost(post) {
788
804
  spoiler: post.spoiler
789
805
  };
790
806
  }
807
+ var init_formatters = __esm({
808
+ "src/utils/formatters.ts"() {
809
+ "use strict";
810
+ init_cjs_shims();
811
+ }
812
+ });
791
813
 
792
814
  // src/tools/user-tools.ts
793
- var import_types = require("@modelcontextprotocol/sdk/types.js");
794
815
  async function getUserInfo(params) {
795
816
  const { username } = params;
796
817
  const client = getRedditClient();
@@ -910,9 +931,18 @@ ${body}`;
910
931
  throw new import_types.McpError(import_types.ErrorCode.InternalError, `Failed to fetch user comments: ${String(error)}`);
911
932
  }
912
933
  }
934
+ var import_types;
935
+ var init_user_tools = __esm({
936
+ "src/tools/user-tools.ts"() {
937
+ "use strict";
938
+ init_cjs_shims();
939
+ init_reddit_client();
940
+ init_formatters();
941
+ import_types = require("@modelcontextprotocol/sdk/types.js");
942
+ }
943
+ });
913
944
 
914
945
  // src/tools/post-tools.ts
915
- var import_types2 = require("@modelcontextprotocol/sdk/types.js");
916
946
  async function getRedditPost(params) {
917
947
  const { subreddit, post_id } = params;
918
948
  const client = getRedditClient();
@@ -1061,9 +1091,18 @@ Your reply has been successfully posted.
1061
1091
  throw new import_types2.McpError(import_types2.ErrorCode.InternalError, `Failed to reply to post: ${String(error)}`);
1062
1092
  }
1063
1093
  }
1094
+ var import_types2;
1095
+ var init_post_tools = __esm({
1096
+ "src/tools/post-tools.ts"() {
1097
+ "use strict";
1098
+ init_cjs_shims();
1099
+ init_reddit_client();
1100
+ init_formatters();
1101
+ import_types2 = require("@modelcontextprotocol/sdk/types.js");
1102
+ }
1103
+ });
1064
1104
 
1065
1105
  // src/tools/subreddit-tools.ts
1066
- var import_types3 = require("@modelcontextprotocol/sdk/types.js");
1067
1106
  async function getSubredditInfo(params) {
1068
1107
  const { subreddit_name } = params;
1069
1108
  const client = getRedditClient();
@@ -1136,9 +1175,18 @@ ${trendingSubreddits.map((subreddit, index) => `${index + 1}. r/${subreddit}`).j
1136
1175
  throw new import_types3.McpError(import_types3.ErrorCode.InternalError, `Failed to fetch trending subreddits: ${String(error)}`);
1137
1176
  }
1138
1177
  }
1178
+ var import_types3;
1179
+ var init_subreddit_tools = __esm({
1180
+ "src/tools/subreddit-tools.ts"() {
1181
+ "use strict";
1182
+ init_cjs_shims();
1183
+ init_reddit_client();
1184
+ init_formatters();
1185
+ import_types3 = require("@modelcontextprotocol/sdk/types.js");
1186
+ }
1187
+ });
1139
1188
 
1140
1189
  // src/tools/search-tools.ts
1141
- var import_types4 = require("@modelcontextprotocol/sdk/types.js");
1142
1190
  async function searchReddit(params) {
1143
1191
  const { query, subreddit, sort = "relevance", time_filter = "all", limit = 10, type = "link" } = params;
1144
1192
  const client = getRedditClient();
@@ -1191,9 +1239,18 @@ ${formatted.spoiler ? "- **Spoiler**" : ""}
1191
1239
  throw new import_types4.McpError(import_types4.ErrorCode.InternalError, `Failed to search Reddit: ${String(error)}`);
1192
1240
  }
1193
1241
  }
1242
+ var import_types4;
1243
+ var init_search_tools = __esm({
1244
+ "src/tools/search-tools.ts"() {
1245
+ "use strict";
1246
+ init_cjs_shims();
1247
+ init_reddit_client();
1248
+ init_formatters();
1249
+ import_types4 = require("@modelcontextprotocol/sdk/types.js");
1250
+ }
1251
+ });
1194
1252
 
1195
1253
  // src/tools/comment-tools.ts
1196
- var import_types5 = require("@modelcontextprotocol/sdk/types.js");
1197
1254
  async function getPostComments(params) {
1198
1255
  const { post_id, subreddit, sort = "best", limit = 100 } = params;
1199
1256
  const client = getRedditClient();
@@ -1244,439 +1301,519 @@ ${comments.map((comment) => formatComment(comment)).join("\n\n---\n\n")}`
1244
1301
  throw new import_types5.McpError(import_types5.ErrorCode.InternalError, `Failed to fetch comments: ${String(error)}`);
1245
1302
  }
1246
1303
  }
1247
-
1248
- // src/index.ts
1249
- var import_dotenv = __toESM(require("dotenv"));
1250
- import_dotenv.default.config();
1251
- var RedditServer = class {
1252
- server;
1253
- constructor() {
1254
- this.server = new import_server.Server(
1255
- {
1256
- name: "reddit-mcp-server",
1257
- version: "0.1.0"
1258
- },
1259
- {
1260
- capabilities: {
1261
- tools: {}
1262
- }
1263
- }
1264
- );
1265
- this.initializeRedditClient();
1266
- this.setupToolHandlers();
1267
- this.server.onerror = async (error) => {
1268
- await this.server.sendLoggingMessage({
1269
- level: "error",
1270
- logger: "reddit-server",
1271
- data: `Server error: ${error}`
1272
- });
1273
- };
1274
- process.on("SIGINT", async () => {
1275
- await this.server.close();
1276
- process.exit(0);
1277
- });
1304
+ var import_types5;
1305
+ var init_comment_tools = __esm({
1306
+ "src/tools/comment-tools.ts"() {
1307
+ "use strict";
1308
+ init_cjs_shims();
1309
+ init_reddit_client();
1310
+ init_formatters();
1311
+ import_types5 = require("@modelcontextprotocol/sdk/types.js");
1278
1312
  }
1279
- initializeRedditClient() {
1280
- const clientId = process.env.REDDIT_CLIENT_ID;
1281
- const clientSecret = process.env.REDDIT_CLIENT_SECRET;
1282
- const userAgent = process.env.REDDIT_USER_AGENT || "RedditMCPServer/0.1.0";
1283
- const username = process.env.REDDIT_USERNAME;
1284
- const password = process.env.REDDIT_PASSWORD;
1285
- if (!clientId || !clientSecret) {
1286
- process.exit(1);
1287
- }
1288
- try {
1289
- initializeRedditClient({
1290
- clientId,
1291
- clientSecret,
1292
- userAgent,
1293
- username,
1294
- password
1295
- });
1296
- } catch {
1297
- process.exit(1);
1298
- }
1313
+ });
1314
+
1315
+ // src/tools/index.ts
1316
+ var init_tools = __esm({
1317
+ "src/tools/index.ts"() {
1318
+ "use strict";
1319
+ init_cjs_shims();
1320
+ init_user_tools();
1321
+ init_post_tools();
1322
+ init_subreddit_tools();
1323
+ init_search_tools();
1324
+ init_comment_tools();
1299
1325
  }
1300
- setupToolHandlers() {
1301
- this.server.setRequestHandler(import_types6.ListToolsRequestSchema, async () => ({
1302
- tools: [
1303
- {
1304
- name: "test_reddit_mcp_server",
1305
- description: "Test the Reddit MCP Server",
1306
- inputSchema: {
1307
- type: "object",
1308
- properties: {
1309
- // No input parameters, this will just return a test message
1326
+ });
1327
+
1328
+ // src/index.ts
1329
+ var src_exports = {};
1330
+ __export(src_exports, {
1331
+ RedditServer: () => RedditServer
1332
+ });
1333
+ var import_server, import_stdio, import_types6, import_dotenv, RedditServer;
1334
+ var init_src = __esm({
1335
+ "src/index.ts"() {
1336
+ "use strict";
1337
+ init_cjs_shims();
1338
+ import_server = require("@modelcontextprotocol/sdk/server/index.js");
1339
+ import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
1340
+ import_types6 = require("@modelcontextprotocol/sdk/types.js");
1341
+ init_reddit_client();
1342
+ init_tools();
1343
+ import_dotenv = __toESM(require("dotenv"));
1344
+ import_dotenv.default.config();
1345
+ RedditServer = class {
1346
+ server;
1347
+ constructor() {
1348
+ this.server = new import_server.Server(
1349
+ {
1350
+ name: "reddit-mcp-server",
1351
+ version: "0.1.0"
1352
+ },
1353
+ {
1354
+ capabilities: {
1355
+ tools: {},
1356
+ logging: {}
1310
1357
  }
1311
1358
  }
1312
- },
1313
- {
1314
- name: "get_reddit_post",
1315
- description: "Get a Reddit post",
1316
- inputSchema: {
1317
- type: "object",
1318
- properties: {
1319
- subreddit: {
1320
- type: "string",
1321
- description: "The subreddit to fetch posts from"
1322
- },
1323
- post_id: {
1324
- type: "string",
1325
- description: "The ID of the post to fetch"
1359
+ );
1360
+ this.initializeRedditClient();
1361
+ this.setupToolHandlers();
1362
+ this.server.onerror = async (error) => {
1363
+ await this.server.sendLoggingMessage({
1364
+ level: "error",
1365
+ logger: "reddit-server",
1366
+ data: `Server error: ${error}`
1367
+ });
1368
+ };
1369
+ process.on("SIGINT", async () => {
1370
+ await this.server.close();
1371
+ process.exit(0);
1372
+ });
1373
+ }
1374
+ initializeRedditClient() {
1375
+ const clientId = process.env.REDDIT_CLIENT_ID;
1376
+ const clientSecret = process.env.REDDIT_CLIENT_SECRET;
1377
+ const userAgent = process.env.REDDIT_USER_AGENT || "RedditMCPServer/0.1.0";
1378
+ const username = process.env.REDDIT_USERNAME;
1379
+ const password = process.env.REDDIT_PASSWORD;
1380
+ if (!clientId || !clientSecret) {
1381
+ console.error(
1382
+ "[Error] Missing required Reddit API credentials. Please set REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET environment variables."
1383
+ );
1384
+ process.exit(1);
1385
+ }
1386
+ try {
1387
+ initializeRedditClient({
1388
+ clientId,
1389
+ clientSecret,
1390
+ userAgent,
1391
+ username,
1392
+ password
1393
+ });
1394
+ console.error("[Setup] Reddit client initialized");
1395
+ if (username && password) {
1396
+ console.error(`[Setup] Authenticated as user: ${username}`);
1397
+ } else {
1398
+ console.error("[Setup] Running in read-only mode (no user authentication)");
1399
+ }
1400
+ } catch (error) {
1401
+ console.error("[Error] Failed to initialize Reddit client:", error);
1402
+ process.exit(1);
1403
+ }
1404
+ }
1405
+ setupToolHandlers() {
1406
+ this.server.setRequestHandler(import_types6.ListToolsRequestSchema, async () => ({
1407
+ tools: [
1408
+ {
1409
+ name: "test_reddit_mcp_server",
1410
+ description: "Test the Reddit MCP Server",
1411
+ inputSchema: {
1412
+ type: "object",
1413
+ properties: {
1414
+ // No input parameters, this will just return a test message
1415
+ }
1326
1416
  }
1327
1417
  },
1328
- required: ["subreddit", "post_id"]
1329
- }
1330
- },
1331
- {
1332
- name: "get_top_posts",
1333
- description: "Get top posts from a subreddit",
1334
- inputSchema: {
1335
- type: "object",
1336
- properties: {
1337
- subreddit: {
1338
- type: "string",
1339
- description: "Name of the subreddit"
1340
- },
1341
- time_filter: {
1342
- type: "string",
1343
- description: "Time period to filter posts (e.g. 'day', 'week', 'month', 'year', 'all')",
1344
- enum: ["day", "week", "month", "year", "all"],
1345
- default: "week"
1346
- },
1347
- limit: {
1348
- type: "integer",
1349
- description: "Number of posts to fetch",
1350
- default: 10
1418
+ {
1419
+ name: "get_reddit_post",
1420
+ description: "Get a Reddit post",
1421
+ inputSchema: {
1422
+ type: "object",
1423
+ properties: {
1424
+ subreddit: {
1425
+ type: "string",
1426
+ description: "The subreddit to fetch posts from"
1427
+ },
1428
+ post_id: {
1429
+ type: "string",
1430
+ description: "The ID of the post to fetch"
1431
+ }
1432
+ },
1433
+ required: ["subreddit", "post_id"]
1351
1434
  }
1352
1435
  },
1353
- required: ["subreddit"]
1354
- }
1355
- },
1356
- {
1357
- name: "get_user_info",
1358
- description: "Get information about a Reddit user",
1359
- inputSchema: {
1360
- type: "object",
1361
- properties: {
1362
- username: {
1363
- type: "string",
1364
- description: "The username of the Reddit user to get info for"
1436
+ {
1437
+ name: "get_top_posts",
1438
+ description: "Get top posts from a subreddit",
1439
+ inputSchema: {
1440
+ type: "object",
1441
+ properties: {
1442
+ subreddit: {
1443
+ type: "string",
1444
+ description: "Name of the subreddit"
1445
+ },
1446
+ time_filter: {
1447
+ type: "string",
1448
+ description: "Time period to filter posts (e.g. 'day', 'week', 'month', 'year', 'all')",
1449
+ enum: ["day", "week", "month", "year", "all"],
1450
+ default: "week"
1451
+ },
1452
+ limit: {
1453
+ type: "integer",
1454
+ description: "Number of posts to fetch",
1455
+ default: 10
1456
+ }
1457
+ },
1458
+ required: ["subreddit"]
1365
1459
  }
1366
1460
  },
1367
- required: ["username"]
1368
- }
1369
- },
1370
- {
1371
- name: "get_subreddit_info",
1372
- description: "Get information about a subreddit",
1373
- inputSchema: {
1374
- type: "object",
1375
- properties: {
1376
- subreddit_name: {
1377
- type: "string",
1378
- description: "Name of the subreddit"
1461
+ {
1462
+ name: "get_user_info",
1463
+ description: "Get information about a Reddit user",
1464
+ inputSchema: {
1465
+ type: "object",
1466
+ properties: {
1467
+ username: {
1468
+ type: "string",
1469
+ description: "The username of the Reddit user to get info for"
1470
+ }
1471
+ },
1472
+ required: ["username"]
1379
1473
  }
1380
1474
  },
1381
- required: ["subreddit_name"]
1382
- }
1383
- },
1384
- {
1385
- name: "get_trending_subreddits",
1386
- description: "Get currently trending subreddits",
1387
- inputSchema: {
1388
- type: "object",
1389
- properties: {}
1390
- }
1391
- },
1392
- {
1393
- name: "create_post",
1394
- description: "Create a new post in a subreddit",
1395
- inputSchema: {
1396
- type: "object",
1397
- properties: {
1398
- subreddit: {
1399
- type: "string",
1400
- description: "Name of the subreddit to post in"
1401
- },
1402
- title: {
1403
- type: "string",
1404
- description: "Title of the post"
1405
- },
1406
- content: {
1407
- type: "string",
1408
- description: "Content of the post (text for self posts, URL for link posts)"
1409
- },
1410
- is_self: {
1411
- type: "boolean",
1412
- description: "Whether this is a self (text) post (true) or link post (false)",
1413
- default: true
1475
+ {
1476
+ name: "get_subreddit_info",
1477
+ description: "Get information about a subreddit",
1478
+ inputSchema: {
1479
+ type: "object",
1480
+ properties: {
1481
+ subreddit_name: {
1482
+ type: "string",
1483
+ description: "Name of the subreddit"
1484
+ }
1485
+ },
1486
+ required: ["subreddit_name"]
1414
1487
  }
1415
1488
  },
1416
- required: ["subreddit", "title", "content"]
1417
- }
1418
- },
1419
- {
1420
- name: "reply_to_post",
1421
- description: "Post a reply to an existing Reddit post",
1422
- inputSchema: {
1423
- type: "object",
1424
- properties: {
1425
- post_id: {
1426
- type: "string",
1427
- description: "The ID of the post to reply to"
1428
- },
1429
- content: {
1430
- type: "string",
1431
- description: "The content of the reply"
1432
- },
1433
- subreddit: {
1434
- type: "string",
1435
- description: "The subreddit name if known (for validation)"
1489
+ {
1490
+ name: "get_trending_subreddits",
1491
+ description: "Get currently trending subreddits",
1492
+ inputSchema: {
1493
+ type: "object",
1494
+ properties: {}
1436
1495
  }
1437
1496
  },
1438
- required: ["post_id", "content"]
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"
1497
+ {
1498
+ name: "create_post",
1499
+ description: "Create a new post in a subreddit",
1500
+ inputSchema: {
1501
+ type: "object",
1502
+ properties: {
1503
+ subreddit: {
1504
+ type: "string",
1505
+ description: "Name of the subreddit to post in"
1506
+ },
1507
+ title: {
1508
+ type: "string",
1509
+ description: "Title of the post"
1510
+ },
1511
+ content: {
1512
+ type: "string",
1513
+ description: "Content of the post (text for self posts, URL for link posts)"
1514
+ },
1515
+ is_self: {
1516
+ type: "boolean",
1517
+ description: "Whether this is a self (text) post (true) or link post (false)",
1518
+ default: true
1519
+ }
1520
+ },
1521
+ required: ["subreddit", "title", "content"]
1479
1522
  }
1480
1523
  },
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
1524
+ {
1525
+ name: "reply_to_post",
1526
+ description: "Post a reply to an existing Reddit post",
1527
+ inputSchema: {
1528
+ type: "object",
1529
+ properties: {
1530
+ post_id: {
1531
+ type: "string",
1532
+ description: "The ID of the post to reply to"
1533
+ },
1534
+ content: {
1535
+ type: "string",
1536
+ description: "The content of the reply"
1537
+ },
1538
+ subreddit: {
1539
+ type: "string",
1540
+ description: "The subreddit name if known (for validation)"
1541
+ }
1542
+ },
1543
+ required: ["post_id", "content"]
1510
1544
  }
1511
1545
  },
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
1546
+ {
1547
+ name: "search_reddit",
1548
+ description: "Search for posts on Reddit",
1549
+ inputSchema: {
1550
+ type: "object",
1551
+ properties: {
1552
+ query: {
1553
+ type: "string",
1554
+ description: "The search query"
1555
+ },
1556
+ subreddit: {
1557
+ type: "string",
1558
+ description: "Search within a specific subreddit (optional)"
1559
+ },
1560
+ sort: {
1561
+ type: "string",
1562
+ description: "Sort order: relevance, hot, top, new, comments",
1563
+ enum: ["relevance", "hot", "top", "new", "comments"],
1564
+ default: "relevance"
1565
+ },
1566
+ time_filter: {
1567
+ type: "string",
1568
+ description: "Time filter: hour, day, week, month, year, all",
1569
+ enum: ["hour", "day", "week", "month", "year", "all"],
1570
+ default: "all"
1571
+ },
1572
+ limit: {
1573
+ type: "number",
1574
+ description: "Maximum number of results to return",
1575
+ minimum: 1,
1576
+ maximum: 100,
1577
+ default: 10
1578
+ },
1579
+ type: {
1580
+ type: "string",
1581
+ description: "Type of content: link (posts), sr (subreddits), user (users)",
1582
+ enum: ["link", "sr", "user"],
1583
+ default: "link"
1584
+ }
1585
+ },
1586
+ required: ["query"]
1543
1587
  }
1544
1588
  },
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
1589
+ {
1590
+ name: "get_post_comments",
1591
+ description: "Get comments for a specific Reddit post",
1592
+ inputSchema: {
1593
+ type: "object",
1594
+ properties: {
1595
+ post_id: {
1596
+ type: "string",
1597
+ description: "The ID of the post"
1598
+ },
1599
+ subreddit: {
1600
+ type: "string",
1601
+ description: "The subreddit where the post is located"
1602
+ },
1603
+ sort: {
1604
+ type: "string",
1605
+ description: "Comment sort order: best, top, new, controversial, old, qa",
1606
+ enum: ["best", "top", "new", "controversial", "old", "qa"],
1607
+ default: "best"
1608
+ },
1609
+ limit: {
1610
+ type: "number",
1611
+ description: "Maximum number of comments to load",
1612
+ minimum: 1,
1613
+ maximum: 500,
1614
+ default: 100
1615
+ }
1616
+ },
1617
+ required: ["post_id", "subreddit"]
1576
1618
  }
1577
1619
  },
1578
- required: ["username"]
1620
+ {
1621
+ name: "get_user_posts",
1622
+ description: "Get posts submitted by a specific user",
1623
+ inputSchema: {
1624
+ type: "object",
1625
+ properties: {
1626
+ username: {
1627
+ type: "string",
1628
+ description: "The username to get posts for"
1629
+ },
1630
+ sort: {
1631
+ type: "string",
1632
+ description: "Sort order: new, hot, top, controversial",
1633
+ enum: ["new", "hot", "top", "controversial"],
1634
+ default: "new"
1635
+ },
1636
+ time_filter: {
1637
+ type: "string",
1638
+ description: "Time filter for top/controversial: hour, day, week, month, year, all",
1639
+ enum: ["hour", "day", "week", "month", "year", "all"],
1640
+ default: "all"
1641
+ },
1642
+ limit: {
1643
+ type: "number",
1644
+ description: "Maximum number of posts to return",
1645
+ minimum: 1,
1646
+ maximum: 100,
1647
+ default: 10
1648
+ }
1649
+ },
1650
+ required: ["username"]
1651
+ }
1652
+ },
1653
+ {
1654
+ name: "get_user_comments",
1655
+ description: "Get comments made by a specific user",
1656
+ inputSchema: {
1657
+ type: "object",
1658
+ properties: {
1659
+ username: {
1660
+ type: "string",
1661
+ description: "The username to get comments for"
1662
+ },
1663
+ sort: {
1664
+ type: "string",
1665
+ description: "Sort order: new, hot, top, controversial",
1666
+ enum: ["new", "hot", "top", "controversial"],
1667
+ default: "new"
1668
+ },
1669
+ time_filter: {
1670
+ type: "string",
1671
+ description: "Time filter for top/controversial: hour, day, week, month, year, all",
1672
+ enum: ["hour", "day", "week", "month", "year", "all"],
1673
+ default: "all"
1674
+ },
1675
+ limit: {
1676
+ type: "number",
1677
+ description: "Maximum number of comments to return",
1678
+ minimum: 1,
1679
+ maximum: 100,
1680
+ default: 10
1681
+ }
1682
+ },
1683
+ required: ["username"]
1684
+ }
1685
+ }
1686
+ ]
1687
+ }));
1688
+ this.server.setRequestHandler(import_types6.CallToolRequestSchema, async (request) => {
1689
+ try {
1690
+ const toolName = request.params.name;
1691
+ const toolParams = request.params.arguments || {};
1692
+ await this.server.sendLoggingMessage({
1693
+ level: "debug",
1694
+ logger: "reddit-server",
1695
+ data: `Tool call: ${toolName}`
1696
+ });
1697
+ switch (toolName) {
1698
+ case "test_reddit_mcp_server":
1699
+ return {
1700
+ content: [
1701
+ {
1702
+ type: "text",
1703
+ text: "Hello, world! The Reddit MCP Server is working correctly."
1704
+ }
1705
+ ]
1706
+ };
1707
+ case "get_reddit_post":
1708
+ return await getRedditPost(toolParams);
1709
+ case "get_top_posts":
1710
+ return await getTopPosts(
1711
+ toolParams
1712
+ );
1713
+ case "get_user_info":
1714
+ return await getUserInfo(toolParams);
1715
+ case "get_subreddit_info":
1716
+ return await getSubredditInfo(toolParams);
1717
+ case "get_trending_subreddits":
1718
+ return await getTrendingSubreddits();
1719
+ case "create_post":
1720
+ return await createPost(
1721
+ toolParams
1722
+ );
1723
+ case "reply_to_post":
1724
+ return await replyToPost(
1725
+ toolParams
1726
+ );
1727
+ case "search_reddit":
1728
+ return await searchReddit(
1729
+ toolParams
1730
+ );
1731
+ case "get_post_comments":
1732
+ return await getPostComments(
1733
+ toolParams
1734
+ );
1735
+ case "get_user_posts":
1736
+ return await getUserPosts(
1737
+ toolParams
1738
+ );
1739
+ case "get_user_comments":
1740
+ return await getUserComments(
1741
+ toolParams
1742
+ );
1743
+ default:
1744
+ throw new import_types6.McpError(import_types6.ErrorCode.MethodNotFound, `Tool with name ${toolName} not found`);
1745
+ }
1746
+ } catch (error) {
1747
+ if (error instanceof Error) {
1748
+ await this.server.sendLoggingMessage({
1749
+ level: "error",
1750
+ logger: "reddit-server",
1751
+ data: `Error calling tool: ${error.message}`
1752
+ });
1753
+ throw new import_types6.McpError(import_types6.ErrorCode.InternalError, `Failed to fetch data: ${error.message}`);
1754
+ }
1755
+ throw error;
1579
1756
  }
1580
- }
1581
- ]
1582
- }));
1583
- this.server.setRequestHandler(import_types6.CallToolRequestSchema, async (request) => {
1584
- try {
1585
- const toolName = request.params.name;
1586
- const toolParams = request.params.arguments || {};
1757
+ });
1758
+ }
1759
+ async run() {
1760
+ const transport = new import_stdio.StdioServerTransport();
1761
+ await this.server.connect(transport);
1587
1762
  await this.server.sendLoggingMessage({
1588
- level: "debug",
1763
+ level: "info",
1589
1764
  logger: "reddit-server",
1590
- data: `Tool call: ${toolName}`
1765
+ data: "Reddit MCP Server is running"
1766
+ });
1767
+ const username = process.env.REDDIT_USERNAME;
1768
+ const password = process.env.REDDIT_PASSWORD;
1769
+ await this.server.sendLoggingMessage({
1770
+ level: "info",
1771
+ logger: "reddit-server",
1772
+ data: username && password ? `Authenticated as user: ${username}` : "Running in read-only mode (no user authentication)"
1591
1773
  });
1592
- switch (toolName) {
1593
- case "test_reddit_mcp_server":
1594
- return {
1595
- content: [
1596
- {
1597
- type: "text",
1598
- text: "Hello, world! The Reddit MCP Server is working correctly."
1599
- }
1600
- ]
1601
- };
1602
- case "get_reddit_post":
1603
- return await getRedditPost(toolParams);
1604
- case "get_top_posts":
1605
- return await getTopPosts(
1606
- toolParams
1607
- );
1608
- case "get_user_info":
1609
- return await getUserInfo(toolParams);
1610
- case "get_subreddit_info":
1611
- return await getSubredditInfo(toolParams);
1612
- case "get_trending_subreddits":
1613
- return await getTrendingSubreddits();
1614
- case "create_post":
1615
- return await createPost(
1616
- toolParams
1617
- );
1618
- case "reply_to_post":
1619
- return await replyToPost(
1620
- toolParams
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
- );
1638
- default:
1639
- throw new import_types6.McpError(import_types6.ErrorCode.MethodNotFound, `Tool with name ${toolName} not found`);
1640
- }
1641
- } catch (error) {
1642
- if (error instanceof Error) {
1643
- await this.server.sendLoggingMessage({
1644
- level: "error",
1645
- logger: "reddit-server",
1646
- data: `Error calling tool: ${error.message}`
1647
- });
1648
- throw new import_types6.McpError(import_types6.ErrorCode.InternalError, `Failed to fetch data: ${error.message}`);
1649
- }
1650
- throw error;
1651
1774
  }
1652
- });
1653
- }
1654
- async run() {
1655
- const transport = new import_stdio.StdioServerTransport();
1656
- await this.server.connect(transport);
1657
- await this.server.sendLoggingMessage({
1658
- level: "info",
1659
- logger: "reddit-server",
1660
- data: "Reddit MCP Server is running"
1661
- });
1662
- const username = process.env.REDDIT_USERNAME;
1663
- const password = process.env.REDDIT_PASSWORD;
1664
- await this.server.sendLoggingMessage({
1665
- level: "info",
1666
- logger: "reddit-server",
1667
- data: username && password ? `Authenticated as user: ${username}` : "Running in read-only mode (no user authentication)"
1668
- });
1775
+ };
1669
1776
  }
1670
- };
1671
- if (require.main === module) {
1672
- const server2 = new RedditServer();
1673
- server2.run().catch(() => {
1777
+ });
1778
+
1779
+ // src/bin.ts
1780
+ init_cjs_shims();
1781
+ var import_fs = __toESM(require("fs"));
1782
+ var import_path = __toESM(require("path"));
1783
+ var packageJsonPath = import_path.default.join(__dirname, "..", "package.json");
1784
+ var packageJson = JSON.parse(import_fs.default.readFileSync(packageJsonPath, "utf-8"));
1785
+ var args = process.argv.slice(2);
1786
+ if (args.includes("--version") || args.includes("-v")) {
1787
+ console.log(packageJson.version);
1788
+ process.exit(0);
1789
+ }
1790
+ if (args.includes("--help") || args.includes("-h")) {
1791
+ console.log(`
1792
+ Reddit MCP Server v${packageJson.version}
1793
+
1794
+ Usage: reddit-mcp-server [options]
1795
+
1796
+ Options:
1797
+ -v, --version Show version number
1798
+ -h, --help Show help
1799
+
1800
+ Environment Variables:
1801
+ REDDIT_CLIENT_ID Reddit API client ID (required)
1802
+ REDDIT_CLIENT_SECRET Reddit API client secret (required)
1803
+ REDDIT_USERNAME Reddit username (optional, for write operations)
1804
+ REDDIT_PASSWORD Reddit password (optional, for write operations)
1805
+ REDDIT_USER_AGENT Custom user agent (optional)
1806
+
1807
+ For more information, visit: https://github.com/jordanburke/reddit-mcp-server
1808
+ `);
1809
+ process.exit(0);
1810
+ }
1811
+ async function main() {
1812
+ const { RedditServer: RedditServer2 } = await Promise.resolve().then(() => (init_src(), src_exports));
1813
+ const server = new RedditServer2();
1814
+ server.run().catch((error) => {
1815
+ console.error(error);
1674
1816
  process.exit(1);
1675
1817
  });
1676
1818
  }
1677
-
1678
- // src/bin.ts
1679
- var server = new RedditServer();
1680
- server.run().catch(() => {
1681
- process.exit(1);
1682
- });
1819
+ main().then();