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.
- package/dist/bin.js +1036 -899
- package/dist/index.js +13 -8
- 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
|
-
//
|
|
27
|
-
var
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
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
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
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
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
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
|
-
|
|
512
|
-
|
|
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
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
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
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
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
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
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
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
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
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
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
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
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
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
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
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
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
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
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
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
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
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
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
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
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
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1584
|
-
|
|
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: "
|
|
1763
|
+
level: "info",
|
|
1589
1764
|
logger: "reddit-server",
|
|
1590
|
-
data:
|
|
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
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
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();
|