reddit-mcp-server 1.0.2 → 1.0.3
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/.claude/settings.local.json +4 -1
- package/package.json +4 -4
- package/src/client/reddit-client.ts +11 -11
- package/src/index.ts +44 -15
- package/src/tools/post-tools.ts +8 -8
- package/src/tools/subreddit-tools.ts +4 -4
- package/src/tools/user-tools.ts +2 -2
- package/dist/bin.d.ts +0 -1
- package/dist/bin.js +0 -1105
- package/dist/index.d.ts +0 -9
- package/dist/index.js +0 -1114
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reddit-mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "A Model Context Protocol (MCP) that provides tools for fetching and creating Reddit content. Fork of the alexandros-lekkas/reddit-mcp-server.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"author": "Jordan Burke <jordan.burke@gmail.com>",
|
|
16
16
|
"license": "ISC",
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@modelcontextprotocol/sdk": "^1.13.
|
|
18
|
+
"@modelcontextprotocol/sdk": "^1.13.2",
|
|
19
19
|
"axios": "^1.10.0",
|
|
20
|
-
"dotenv": "^16.
|
|
20
|
+
"dotenv": "^16.6.1"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@eslint/js": "^9.29.0",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"eslint": "^9.29.0",
|
|
28
28
|
"eslint-config-prettier": "^10.1.5",
|
|
29
29
|
"eslint-plugin-prettier": "^5.5.1",
|
|
30
|
-
"prettier": "^3.6.
|
|
30
|
+
"prettier": "^3.6.2",
|
|
31
31
|
"tsup": "^8.5.0",
|
|
32
32
|
"typescript": "^5.8.3"
|
|
33
33
|
},
|
|
@@ -52,12 +52,12 @@ export class RedditClient {
|
|
|
52
52
|
const authData = new URLSearchParams()
|
|
53
53
|
|
|
54
54
|
if (this.username && this.password) {
|
|
55
|
-
|
|
55
|
+
// Authenticating with user credentials
|
|
56
56
|
authData.append("grant_type", "password")
|
|
57
57
|
authData.append("username", this.username)
|
|
58
58
|
authData.append("password", this.password)
|
|
59
59
|
} else {
|
|
60
|
-
|
|
60
|
+
// Authenticating with client credentials (read-only)
|
|
61
61
|
authData.append("grant_type", "client_credentials")
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -77,9 +77,9 @@ export class RedditClient {
|
|
|
77
77
|
this.authenticated = true
|
|
78
78
|
this.api.defaults.headers.common["Authorization"] = `Bearer ${this.accessToken}`
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
// Successfully authenticated with Reddit API
|
|
81
81
|
} catch (error) {
|
|
82
|
-
|
|
82
|
+
// Authentication error occurred
|
|
83
83
|
throw new Error("Failed to authenticate with Reddit API")
|
|
84
84
|
}
|
|
85
85
|
}
|
|
@@ -115,7 +115,7 @@ export class RedditClient {
|
|
|
115
115
|
profileUrl: `https://reddit.com/user/${data.name}`,
|
|
116
116
|
}
|
|
117
117
|
} catch (error) {
|
|
118
|
-
|
|
118
|
+
// Failed to get user info
|
|
119
119
|
throw new Error(`Failed to get user info for ${username}`)
|
|
120
120
|
}
|
|
121
121
|
}
|
|
@@ -139,7 +139,7 @@ export class RedditClient {
|
|
|
139
139
|
url: data.url,
|
|
140
140
|
}
|
|
141
141
|
} catch (error) {
|
|
142
|
-
|
|
142
|
+
// Failed to get subreddit info
|
|
143
143
|
throw new Error(`Failed to get subreddit info for ${subredditName}`)
|
|
144
144
|
}
|
|
145
145
|
}
|
|
@@ -177,7 +177,7 @@ export class RedditClient {
|
|
|
177
177
|
}
|
|
178
178
|
})
|
|
179
179
|
} catch (error) {
|
|
180
|
-
|
|
180
|
+
// Failed to get top posts
|
|
181
181
|
throw new Error(`Failed to get top posts for ${subreddit || "home"}`)
|
|
182
182
|
}
|
|
183
183
|
}
|
|
@@ -220,7 +220,7 @@ export class RedditClient {
|
|
|
220
220
|
permalink: post.permalink,
|
|
221
221
|
}
|
|
222
222
|
} catch (error) {
|
|
223
|
-
|
|
223
|
+
// Failed to get post
|
|
224
224
|
throw new Error(`Failed to get post with ID ${postId}`)
|
|
225
225
|
}
|
|
226
226
|
}
|
|
@@ -234,7 +234,7 @@ export class RedditClient {
|
|
|
234
234
|
|
|
235
235
|
return response.data.data.children.map((child: any) => child.data.display_name)
|
|
236
236
|
} catch (error) {
|
|
237
|
-
|
|
237
|
+
// Failed to get trending subreddits
|
|
238
238
|
throw new Error("Failed to get trending subreddits")
|
|
239
239
|
}
|
|
240
240
|
}
|
|
@@ -268,7 +268,7 @@ export class RedditClient {
|
|
|
268
268
|
throw new Error("Failed to create post")
|
|
269
269
|
}
|
|
270
270
|
} catch (error) {
|
|
271
|
-
|
|
271
|
+
// Failed to create post
|
|
272
272
|
throw new Error(`Failed to create post in ${subreddit}`)
|
|
273
273
|
}
|
|
274
274
|
}
|
|
@@ -321,7 +321,7 @@ export class RedditClient {
|
|
|
321
321
|
permalink: commentData.permalink,
|
|
322
322
|
}
|
|
323
323
|
} catch (error) {
|
|
324
|
-
|
|
324
|
+
// Failed to reply to post
|
|
325
325
|
throw new Error(`Failed to reply to post ${postId}`)
|
|
326
326
|
}
|
|
327
327
|
}
|
package/src/index.ts
CHANGED
|
@@ -12,11 +12,6 @@ class RedditServer {
|
|
|
12
12
|
private server: Server
|
|
13
13
|
|
|
14
14
|
constructor() {
|
|
15
|
-
console.error("[Setup] Initializing Reddit Server...")
|
|
16
|
-
|
|
17
|
-
// Initialize the Reddit client
|
|
18
|
-
this.initializeRedditClient()
|
|
19
|
-
|
|
20
15
|
this.server = new Server(
|
|
21
16
|
{
|
|
22
17
|
name: "reddit-mcp-server",
|
|
@@ -29,9 +24,19 @@ class RedditServer {
|
|
|
29
24
|
},
|
|
30
25
|
)
|
|
31
26
|
|
|
27
|
+
// Initialize the Reddit client
|
|
28
|
+
this.initializeRedditClient()
|
|
29
|
+
|
|
32
30
|
this.setupToolHandlers()
|
|
33
31
|
|
|
34
|
-
this.server.onerror = (error) =>
|
|
32
|
+
this.server.onerror = async (error) => {
|
|
33
|
+
await this.server.sendLoggingMessage({
|
|
34
|
+
level: "error",
|
|
35
|
+
logger: "reddit-server",
|
|
36
|
+
data: `Server error: ${error}`,
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
35
40
|
process.on("SIGINT", async () => {
|
|
36
41
|
await this.server.close()
|
|
37
42
|
process.exit(0)
|
|
@@ -46,6 +51,8 @@ class RedditServer {
|
|
|
46
51
|
const password = process.env.REDDIT_PASSWORD
|
|
47
52
|
|
|
48
53
|
if (!clientId || !clientSecret) {
|
|
54
|
+
// Can't use server logging here as server isn't initialized yet
|
|
55
|
+
// Using console.error is OK for startup failures
|
|
49
56
|
console.error(
|
|
50
57
|
"[Error] Missing required Reddit API credentials. Please set REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET environment variables.",
|
|
51
58
|
)
|
|
@@ -61,13 +68,9 @@ class RedditServer {
|
|
|
61
68
|
password,
|
|
62
69
|
})
|
|
63
70
|
|
|
64
|
-
|
|
65
|
-
if (username && password) {
|
|
66
|
-
console.error(`[Setup] Authenticated as user: ${username}`)
|
|
67
|
-
} else {
|
|
68
|
-
console.error("[Setup] Running in read-only mode (no user authentication)")
|
|
69
|
-
}
|
|
71
|
+
// Client initialized successfully
|
|
70
72
|
} catch (error) {
|
|
73
|
+
// Can't use server logging here as server isn't connected yet
|
|
71
74
|
console.error("[Error] Failed to initialize Reddit client:", error)
|
|
72
75
|
process.exit(1)
|
|
73
76
|
}
|
|
@@ -222,7 +225,12 @@ class RedditServer {
|
|
|
222
225
|
const toolName = request.params.name
|
|
223
226
|
const toolParams = request.params.arguments || {}
|
|
224
227
|
|
|
225
|
-
|
|
228
|
+
// Log tool call
|
|
229
|
+
await this.server.sendLoggingMessage({
|
|
230
|
+
level: "debug",
|
|
231
|
+
logger: "reddit-server",
|
|
232
|
+
data: `Tool call: ${toolName}`,
|
|
233
|
+
})
|
|
226
234
|
|
|
227
235
|
switch (toolName) {
|
|
228
236
|
case "test_reddit_mcp_server":
|
|
@@ -280,7 +288,11 @@ class RedditServer {
|
|
|
280
288
|
}
|
|
281
289
|
} catch (error: unknown) {
|
|
282
290
|
if (error instanceof Error) {
|
|
283
|
-
|
|
291
|
+
await this.server.sendLoggingMessage({
|
|
292
|
+
level: "error",
|
|
293
|
+
logger: "reddit-server",
|
|
294
|
+
data: `Error calling tool: ${error.message}`,
|
|
295
|
+
})
|
|
284
296
|
|
|
285
297
|
throw new McpError(ErrorCode.InternalError, `Failed to fetch data: ${error.message}`)
|
|
286
298
|
}
|
|
@@ -293,7 +305,24 @@ class RedditServer {
|
|
|
293
305
|
async run() {
|
|
294
306
|
const transport = new StdioServerTransport()
|
|
295
307
|
await this.server.connect(transport)
|
|
296
|
-
|
|
308
|
+
|
|
309
|
+
// Log server startup
|
|
310
|
+
await this.server.sendLoggingMessage({
|
|
311
|
+
level: "info",
|
|
312
|
+
logger: "reddit-server",
|
|
313
|
+
data: "Reddit MCP Server is running",
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
// Log authentication status
|
|
317
|
+
const username = process.env.REDDIT_USERNAME
|
|
318
|
+
const password = process.env.REDDIT_PASSWORD
|
|
319
|
+
await this.server.sendLoggingMessage({
|
|
320
|
+
level: "info",
|
|
321
|
+
logger: "reddit-server",
|
|
322
|
+
data: username && password
|
|
323
|
+
? `Authenticated as user: ${username}`
|
|
324
|
+
: "Running in read-only mode (no user authentication)",
|
|
325
|
+
})
|
|
297
326
|
}
|
|
298
327
|
}
|
|
299
328
|
|
package/src/tools/post-tools.ts
CHANGED
|
@@ -11,7 +11,7 @@ export async function getRedditPost(params: { subreddit: string; post_id: string
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
try {
|
|
14
|
-
|
|
14
|
+
// Getting post
|
|
15
15
|
const post = await client.getPost(post_id, subreddit)
|
|
16
16
|
const formattedPost = formatPostInfo(post)
|
|
17
17
|
|
|
@@ -54,7 +54,7 @@ ${formattedPost.bestTimeToEngage}
|
|
|
54
54
|
],
|
|
55
55
|
}
|
|
56
56
|
} catch (error) {
|
|
57
|
-
|
|
57
|
+
// Error will be logged by the server
|
|
58
58
|
throw new McpError(ErrorCode.InternalError, `Failed to fetch post data: ${error}`)
|
|
59
59
|
}
|
|
60
60
|
}
|
|
@@ -68,7 +68,7 @@ export async function getTopPosts(params: { subreddit: string; time_filter?: str
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
try {
|
|
71
|
-
|
|
71
|
+
// Getting top posts
|
|
72
72
|
const posts = await client.getTopPosts(subreddit, time_filter, limit)
|
|
73
73
|
const formattedPosts = posts.map(formatPostInfo)
|
|
74
74
|
|
|
@@ -98,7 +98,7 @@ ${postSummaries}
|
|
|
98
98
|
],
|
|
99
99
|
}
|
|
100
100
|
} catch (error) {
|
|
101
|
-
|
|
101
|
+
// Error will be logged by the server
|
|
102
102
|
throw new McpError(ErrorCode.InternalError, `Failed to fetch top posts: ${error}`)
|
|
103
103
|
}
|
|
104
104
|
}
|
|
@@ -112,7 +112,7 @@ export async function createPost(params: { subreddit: string; title: string; con
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
try {
|
|
115
|
-
|
|
115
|
+
// Creating post
|
|
116
116
|
const post = await client.createPost(subreddit, title, content, is_self)
|
|
117
117
|
const formattedPost = formatPostInfo(post)
|
|
118
118
|
|
|
@@ -135,7 +135,7 @@ Your post has been successfully submitted to r/${formattedPost.subreddit}.
|
|
|
135
135
|
],
|
|
136
136
|
}
|
|
137
137
|
} catch (error) {
|
|
138
|
-
|
|
138
|
+
// Error will be logged by the server
|
|
139
139
|
throw new McpError(ErrorCode.InternalError, `Failed to create post: ${error}`)
|
|
140
140
|
}
|
|
141
141
|
}
|
|
@@ -149,7 +149,7 @@ export async function replyToPost(params: { post_id: string; content: string; su
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
try {
|
|
152
|
-
|
|
152
|
+
// Replying to post
|
|
153
153
|
const comment = await client.replyToPost(post_id, content)
|
|
154
154
|
const formattedComment = formatCommentInfo(comment)
|
|
155
155
|
|
|
@@ -172,7 +172,7 @@ Your reply has been successfully posted.
|
|
|
172
172
|
],
|
|
173
173
|
}
|
|
174
174
|
} catch (error) {
|
|
175
|
-
|
|
175
|
+
// Error will be logged by the server
|
|
176
176
|
throw new McpError(ErrorCode.InternalError, `Failed to reply to post: ${error}`)
|
|
177
177
|
}
|
|
178
178
|
}
|
|
@@ -11,7 +11,7 @@ export async function getSubredditInfo(params: { subreddit_name: string }) {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
try {
|
|
14
|
-
|
|
14
|
+
// Getting subreddit info
|
|
15
15
|
const subreddit = await client.getSubredditInfo(subreddit_name)
|
|
16
16
|
const formattedSubreddit = formatSubredditInfo(subreddit)
|
|
17
17
|
|
|
@@ -56,7 +56,7 @@ ${formattedSubreddit.description.full}
|
|
|
56
56
|
],
|
|
57
57
|
}
|
|
58
58
|
} catch (error) {
|
|
59
|
-
|
|
59
|
+
// Error will be logged by the server
|
|
60
60
|
throw new McpError(ErrorCode.InternalError, `Failed to fetch subreddit data: ${error}`)
|
|
61
61
|
}
|
|
62
62
|
}
|
|
@@ -69,7 +69,7 @@ export async function getTrendingSubreddits() {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
try {
|
|
72
|
-
|
|
72
|
+
// Getting trending subreddits
|
|
73
73
|
const trendingSubreddits = await client.getTrendingSubreddits()
|
|
74
74
|
|
|
75
75
|
return {
|
|
@@ -85,7 +85,7 @@ ${trendingSubreddits.map((subreddit, index) => `${index + 1}. r/${subreddit}`).j
|
|
|
85
85
|
],
|
|
86
86
|
}
|
|
87
87
|
} catch (error) {
|
|
88
|
-
|
|
88
|
+
// Error will be logged by the server
|
|
89
89
|
throw new McpError(ErrorCode.InternalError, `Failed to fetch trending subreddits: ${error}`)
|
|
90
90
|
}
|
|
91
91
|
}
|
package/src/tools/user-tools.ts
CHANGED
|
@@ -11,7 +11,7 @@ export async function getUserInfo(params: { username: string }) {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
try {
|
|
14
|
-
|
|
14
|
+
// Getting user info
|
|
15
15
|
const user = await client.getUser(username)
|
|
16
16
|
const formattedUser = formatUserInfo(user)
|
|
17
17
|
|
|
@@ -42,7 +42,7 @@ export async function getUserInfo(params: { username: string }) {
|
|
|
42
42
|
],
|
|
43
43
|
}
|
|
44
44
|
} catch (error) {
|
|
45
|
-
|
|
45
|
+
// Error will be logged by the server
|
|
46
46
|
throw new McpError(ErrorCode.InternalError, `Failed to fetch user data: ${error}`)
|
|
47
47
|
}
|
|
48
48
|
}
|
package/dist/bin.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|