reddit-mcp-server 1.0.0
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 +18 -0
- package/.env.example +7 -0
- package/.prettierignore +7 -0
- package/.prettierrc +8 -0
- package/CLAUDE.md +104 -0
- package/Dockerfile +17 -0
- package/LICENSE +21 -0
- package/README.md +101 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1200 -0
- package/eslint.config.js +74 -0
- package/package.json +44 -0
- package/pnpm-workspace.yaml +2 -0
- package/smithery.yaml +37 -0
- package/src/client/reddit-client.ts +340 -0
- package/src/index.ts +303 -0
- package/src/tools/index.ts +3 -0
- package/src/tools/post-tools.ts +178 -0
- package/src/tools/subreddit-tools.ts +91 -0
- package/src/tools/user-tools.ts +48 -0
- package/src/types.ts +146 -0
- package/src/utils/formatters.ts +299 -0
- package/tsconfig.json +19 -0
- package/tsup.config.ts +12 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js"
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
5
|
+
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "@modelcontextprotocol/sdk/types.js"
|
|
6
|
+
import { initializeRedditClient } from "./client/reddit-client"
|
|
7
|
+
import * as tools from "./tools"
|
|
8
|
+
import dotenv from "dotenv"
|
|
9
|
+
|
|
10
|
+
// Load environment variables
|
|
11
|
+
dotenv.config()
|
|
12
|
+
|
|
13
|
+
class RedditServer {
|
|
14
|
+
private server: Server
|
|
15
|
+
|
|
16
|
+
constructor() {
|
|
17
|
+
console.log("[Setup] Initializing Reddit Server...")
|
|
18
|
+
|
|
19
|
+
// Initialize the Reddit client
|
|
20
|
+
this.initializeRedditClient()
|
|
21
|
+
|
|
22
|
+
this.server = new Server(
|
|
23
|
+
{
|
|
24
|
+
name: "reddit-mcp-server",
|
|
25
|
+
version: "0.1.0",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
capabilities: {
|
|
29
|
+
tools: {},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
this.setupToolHandlers()
|
|
35
|
+
|
|
36
|
+
this.server.onerror = (error) => console.error("[Error] Server error:", error)
|
|
37
|
+
process.on("SIGINT", async () => {
|
|
38
|
+
await this.server.close()
|
|
39
|
+
process.exit(0)
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private initializeRedditClient() {
|
|
44
|
+
const clientId = process.env.REDDIT_CLIENT_ID
|
|
45
|
+
const clientSecret = process.env.REDDIT_CLIENT_SECRET
|
|
46
|
+
const userAgent = process.env.REDDIT_USER_AGENT || "RedditMCPServer/0.1.0"
|
|
47
|
+
const username = process.env.REDDIT_USERNAME
|
|
48
|
+
const password = process.env.REDDIT_PASSWORD
|
|
49
|
+
|
|
50
|
+
if (!clientId || !clientSecret) {
|
|
51
|
+
console.error(
|
|
52
|
+
"[Error] Missing required Reddit API credentials. Please set REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET environment variables.",
|
|
53
|
+
)
|
|
54
|
+
process.exit(1)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
initializeRedditClient({
|
|
59
|
+
clientId,
|
|
60
|
+
clientSecret,
|
|
61
|
+
userAgent,
|
|
62
|
+
username,
|
|
63
|
+
password,
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
console.log("[Setup] Reddit client initialized")
|
|
67
|
+
if (username && password) {
|
|
68
|
+
console.log(`[Setup] Authenticated as user: ${username}`)
|
|
69
|
+
} else {
|
|
70
|
+
console.log("[Setup] Running in read-only mode (no user authentication)")
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error("[Error] Failed to initialize Reddit client:", error)
|
|
74
|
+
process.exit(1)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private setupToolHandlers() {
|
|
79
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
80
|
+
tools: [
|
|
81
|
+
{
|
|
82
|
+
name: "test_reddit_mcp_server",
|
|
83
|
+
description: "Test the Reddit MCP Server",
|
|
84
|
+
inputSchema: {
|
|
85
|
+
type: "object",
|
|
86
|
+
properties: {
|
|
87
|
+
// No input parameters, this will just return a test message
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: "get_reddit_post",
|
|
93
|
+
description: "Get a Reddit post",
|
|
94
|
+
inputSchema: {
|
|
95
|
+
type: "object",
|
|
96
|
+
properties: {
|
|
97
|
+
subreddit: {
|
|
98
|
+
type: "string",
|
|
99
|
+
description: "The subreddit to fetch posts from",
|
|
100
|
+
},
|
|
101
|
+
post_id: {
|
|
102
|
+
type: "string",
|
|
103
|
+
description: "The ID of the post to fetch",
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
required: ["subreddit", "post_id"],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: "get_top_posts",
|
|
111
|
+
description: "Get top posts from a subreddit",
|
|
112
|
+
inputSchema: {
|
|
113
|
+
type: "object",
|
|
114
|
+
properties: {
|
|
115
|
+
subreddit: {
|
|
116
|
+
type: "string",
|
|
117
|
+
description: "Name of the subreddit",
|
|
118
|
+
},
|
|
119
|
+
time_filter: {
|
|
120
|
+
type: "string",
|
|
121
|
+
description: "Time period to filter posts (e.g. 'day', 'week', 'month', 'year', 'all')",
|
|
122
|
+
enum: ["day", "week", "month", "year", "all"],
|
|
123
|
+
default: "week",
|
|
124
|
+
},
|
|
125
|
+
limit: {
|
|
126
|
+
type: "integer",
|
|
127
|
+
description: "Number of posts to fetch",
|
|
128
|
+
default: 10,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
required: ["subreddit"],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: "get_user_info",
|
|
136
|
+
description: "Get information about a Reddit user",
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: "object",
|
|
139
|
+
properties: {
|
|
140
|
+
username: {
|
|
141
|
+
type: "string",
|
|
142
|
+
description: "The username of the Reddit user to get info for",
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
required: ["username"],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: "get_subreddit_info",
|
|
150
|
+
description: "Get information about a subreddit",
|
|
151
|
+
inputSchema: {
|
|
152
|
+
type: "object",
|
|
153
|
+
properties: {
|
|
154
|
+
subreddit_name: {
|
|
155
|
+
type: "string",
|
|
156
|
+
description: "Name of the subreddit",
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
required: ["subreddit_name"],
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: "get_trending_subreddits",
|
|
164
|
+
description: "Get currently trending subreddits",
|
|
165
|
+
inputSchema: {
|
|
166
|
+
type: "object",
|
|
167
|
+
properties: {},
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: "create_post",
|
|
172
|
+
description: "Create a new post in a subreddit",
|
|
173
|
+
inputSchema: {
|
|
174
|
+
type: "object",
|
|
175
|
+
properties: {
|
|
176
|
+
subreddit: {
|
|
177
|
+
type: "string",
|
|
178
|
+
description: "Name of the subreddit to post in",
|
|
179
|
+
},
|
|
180
|
+
title: {
|
|
181
|
+
type: "string",
|
|
182
|
+
description: "Title of the post",
|
|
183
|
+
},
|
|
184
|
+
content: {
|
|
185
|
+
type: "string",
|
|
186
|
+
description: "Content of the post (text for self posts, URL for link posts)",
|
|
187
|
+
},
|
|
188
|
+
is_self: {
|
|
189
|
+
type: "boolean",
|
|
190
|
+
description: "Whether this is a self (text) post (true) or link post (false)",
|
|
191
|
+
default: true,
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
required: ["subreddit", "title", "content"],
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: "reply_to_post",
|
|
199
|
+
description: "Post a reply to an existing Reddit post",
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {
|
|
203
|
+
post_id: {
|
|
204
|
+
type: "string",
|
|
205
|
+
description: "The ID of the post to reply to",
|
|
206
|
+
},
|
|
207
|
+
content: {
|
|
208
|
+
type: "string",
|
|
209
|
+
description: "The content of the reply",
|
|
210
|
+
},
|
|
211
|
+
subreddit: {
|
|
212
|
+
type: "string",
|
|
213
|
+
description: "The subreddit name if known (for validation)",
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
required: ["post_id", "content"],
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
}))
|
|
221
|
+
|
|
222
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
223
|
+
try {
|
|
224
|
+
const toolName = request.params.name
|
|
225
|
+
const toolParams = request.params.arguments || {}
|
|
226
|
+
|
|
227
|
+
console.log(`[Request] Tool call: ${toolName}`, toolParams)
|
|
228
|
+
|
|
229
|
+
switch (toolName) {
|
|
230
|
+
case "test_reddit_mcp_server":
|
|
231
|
+
return {
|
|
232
|
+
content: [
|
|
233
|
+
{
|
|
234
|
+
type: "text",
|
|
235
|
+
text: "Hello, world! The Reddit MCP Server is working correctly.",
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
case "get_reddit_post":
|
|
241
|
+
return await tools.getRedditPost(toolParams as { subreddit: string; post_id: string })
|
|
242
|
+
|
|
243
|
+
case "get_top_posts":
|
|
244
|
+
return await tools.getTopPosts(
|
|
245
|
+
toolParams as {
|
|
246
|
+
subreddit: string
|
|
247
|
+
time_filter?: string
|
|
248
|
+
limit?: number
|
|
249
|
+
},
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
case "get_user_info":
|
|
253
|
+
return await tools.getUserInfo(toolParams as { username: string })
|
|
254
|
+
|
|
255
|
+
case "get_subreddit_info":
|
|
256
|
+
return await tools.getSubredditInfo(toolParams as { subreddit_name: string })
|
|
257
|
+
|
|
258
|
+
case "get_trending_subreddits":
|
|
259
|
+
return await tools.getTrendingSubreddits()
|
|
260
|
+
|
|
261
|
+
case "create_post":
|
|
262
|
+
return await tools.createPost(
|
|
263
|
+
toolParams as {
|
|
264
|
+
subreddit: string
|
|
265
|
+
title: string
|
|
266
|
+
content: string
|
|
267
|
+
is_self?: boolean
|
|
268
|
+
},
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
case "reply_to_post":
|
|
272
|
+
return await tools.replyToPost(
|
|
273
|
+
toolParams as {
|
|
274
|
+
post_id: string
|
|
275
|
+
content: string
|
|
276
|
+
subreddit?: string
|
|
277
|
+
},
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
default:
|
|
281
|
+
throw new McpError(ErrorCode.MethodNotFound, `Tool with name ${toolName} not found`)
|
|
282
|
+
}
|
|
283
|
+
} catch (error: unknown) {
|
|
284
|
+
if (error instanceof Error) {
|
|
285
|
+
console.error("[Error] Error calling tool:", error.message)
|
|
286
|
+
|
|
287
|
+
throw new McpError(ErrorCode.InternalError, `Failed to fetch data: ${error.message}`)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
throw error
|
|
291
|
+
}
|
|
292
|
+
})
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async run() {
|
|
296
|
+
const transport = new StdioServerTransport()
|
|
297
|
+
await this.server.connect(transport)
|
|
298
|
+
console.log("[Server] Server is running")
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const server = new RedditServer()
|
|
303
|
+
server.run().catch(console.error)
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { getRedditClient } from "../client/reddit-client"
|
|
2
|
+
import { formatPostInfo, formatCommentInfo } from "../utils/formatters"
|
|
3
|
+
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"
|
|
4
|
+
|
|
5
|
+
export async function getRedditPost(params: { subreddit: string; post_id: string }) {
|
|
6
|
+
const { subreddit, post_id } = params
|
|
7
|
+
const client = getRedditClient()
|
|
8
|
+
|
|
9
|
+
if (!client) {
|
|
10
|
+
throw new McpError(ErrorCode.InternalError, "Reddit client not initialized")
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
console.log(`[Tool] Getting post ${post_id} from r/${subreddit}`)
|
|
15
|
+
const post = await client.getPost(post_id, subreddit)
|
|
16
|
+
const formattedPost = formatPostInfo(post)
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: "text",
|
|
22
|
+
text: `
|
|
23
|
+
# Post from r/${formattedPost.subreddit}
|
|
24
|
+
|
|
25
|
+
## Post Details
|
|
26
|
+
- Title: ${formattedPost.title}
|
|
27
|
+
- Type: ${formattedPost.type}
|
|
28
|
+
- Author: u/${formattedPost.author}
|
|
29
|
+
|
|
30
|
+
## Content
|
|
31
|
+
${formattedPost.content}
|
|
32
|
+
|
|
33
|
+
## Stats
|
|
34
|
+
- Score: ${formattedPost.stats.score.toLocaleString()}
|
|
35
|
+
- Upvote Ratio: ${(formattedPost.stats.upvoteRatio * 100).toFixed(1)}%
|
|
36
|
+
- Comments: ${formattedPost.stats.comments.toLocaleString()}
|
|
37
|
+
|
|
38
|
+
## Metadata
|
|
39
|
+
- Posted: ${formattedPost.metadata.posted}
|
|
40
|
+
- Flags: ${formattedPost.metadata.flags.length ? formattedPost.metadata.flags.join(", ") : "None"}
|
|
41
|
+
- Flair: ${formattedPost.metadata.flair}
|
|
42
|
+
|
|
43
|
+
## Links
|
|
44
|
+
- Full Post: ${formattedPost.links.fullPost}
|
|
45
|
+
- Short Link: ${formattedPost.links.shortLink}
|
|
46
|
+
|
|
47
|
+
## Engagement Analysis
|
|
48
|
+
- ${formattedPost.engagementAnalysis.replace(/\n - /g, "\n- ")}
|
|
49
|
+
|
|
50
|
+
## Best Time to Engage
|
|
51
|
+
${formattedPost.bestTimeToEngage}
|
|
52
|
+
`,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error(`[Error] Error getting post: ${error}`)
|
|
58
|
+
throw new McpError(ErrorCode.InternalError, `Failed to fetch post data: ${error}`)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function getTopPosts(params: { subreddit: string; time_filter?: string; limit?: number }) {
|
|
63
|
+
const { subreddit, time_filter = "week", limit = 10 } = params
|
|
64
|
+
const client = getRedditClient()
|
|
65
|
+
|
|
66
|
+
if (!client) {
|
|
67
|
+
throw new McpError(ErrorCode.InternalError, "Reddit client not initialized")
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
console.log(`[Tool] Getting top posts from r/${subreddit}`)
|
|
72
|
+
const posts = await client.getTopPosts(subreddit, time_filter, limit)
|
|
73
|
+
const formattedPosts = posts.map(formatPostInfo)
|
|
74
|
+
|
|
75
|
+
const postSummaries = formattedPosts
|
|
76
|
+
.map(
|
|
77
|
+
(post, index) => `
|
|
78
|
+
### ${index + 1}. ${post.title}
|
|
79
|
+
- Author: u/${post.author}
|
|
80
|
+
- Score: ${post.stats.score.toLocaleString()} (${(post.stats.upvoteRatio * 100).toFixed(1)}% upvoted)
|
|
81
|
+
- Comments: ${post.stats.comments.toLocaleString()}
|
|
82
|
+
- Posted: ${post.metadata.posted}
|
|
83
|
+
- Link: ${post.links.shortLink}
|
|
84
|
+
`,
|
|
85
|
+
)
|
|
86
|
+
.join("\n")
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: "text",
|
|
92
|
+
text: `
|
|
93
|
+
# Top Posts from r/${subreddit} (${time_filter})
|
|
94
|
+
|
|
95
|
+
${postSummaries}
|
|
96
|
+
`,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error(`[Error] Error getting top posts: ${error}`)
|
|
102
|
+
throw new McpError(ErrorCode.InternalError, `Failed to fetch top posts: ${error}`)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function createPost(params: { subreddit: string; title: string; content: string; is_self?: boolean }) {
|
|
107
|
+
const { subreddit, title, content, is_self = true } = params
|
|
108
|
+
const client = getRedditClient()
|
|
109
|
+
|
|
110
|
+
if (!client) {
|
|
111
|
+
throw new McpError(ErrorCode.InternalError, "Reddit client not initialized")
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
console.log(`[Tool] Creating ${is_self ? "text" : "link"} post in r/${subreddit}`)
|
|
116
|
+
const post = await client.createPost(subreddit, title, content, is_self)
|
|
117
|
+
const formattedPost = formatPostInfo(post)
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
content: [
|
|
121
|
+
{
|
|
122
|
+
type: "text",
|
|
123
|
+
text: `
|
|
124
|
+
# Post Created Successfully
|
|
125
|
+
|
|
126
|
+
## Post Details
|
|
127
|
+
- Title: ${formattedPost.title}
|
|
128
|
+
- Subreddit: r/${formattedPost.subreddit}
|
|
129
|
+
- Type: ${formattedPost.type}
|
|
130
|
+
- Link: ${formattedPost.links.fullPost}
|
|
131
|
+
|
|
132
|
+
Your post has been successfully submitted to r/${formattedPost.subreddit}.
|
|
133
|
+
`,
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error(`[Error] Error creating post: ${error}`)
|
|
139
|
+
throw new McpError(ErrorCode.InternalError, `Failed to create post: ${error}`)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function replyToPost(params: { post_id: string; content: string; subreddit?: string }) {
|
|
144
|
+
const { post_id, content } = params
|
|
145
|
+
const client = getRedditClient()
|
|
146
|
+
|
|
147
|
+
if (!client) {
|
|
148
|
+
throw new McpError(ErrorCode.InternalError, "Reddit client not initialized")
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
console.log(`[Tool] Replying to post ${post_id}`)
|
|
153
|
+
const comment = await client.replyToPost(post_id, content)
|
|
154
|
+
const formattedComment = formatCommentInfo(comment)
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
content: [
|
|
158
|
+
{
|
|
159
|
+
type: "text",
|
|
160
|
+
text: `
|
|
161
|
+
# Reply Posted Successfully
|
|
162
|
+
|
|
163
|
+
## Comment Details
|
|
164
|
+
- Author: u/${formattedComment.author}
|
|
165
|
+
- Subreddit: r/${formattedComment.context.subreddit}
|
|
166
|
+
- Thread: ${formattedComment.context.thread}
|
|
167
|
+
- Link: ${formattedComment.link}
|
|
168
|
+
|
|
169
|
+
Your reply has been successfully posted.
|
|
170
|
+
`,
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
}
|
|
174
|
+
} catch (error) {
|
|
175
|
+
console.error(`[Error] Error replying to post: ${error}`)
|
|
176
|
+
throw new McpError(ErrorCode.InternalError, `Failed to reply to post: ${error}`)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { getRedditClient } from "../client/reddit-client"
|
|
2
|
+
import { formatSubredditInfo } from "../utils/formatters"
|
|
3
|
+
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"
|
|
4
|
+
|
|
5
|
+
export async function getSubredditInfo(params: { subreddit_name: string }) {
|
|
6
|
+
const { subreddit_name } = params
|
|
7
|
+
const client = getRedditClient()
|
|
8
|
+
|
|
9
|
+
if (!client) {
|
|
10
|
+
throw new McpError(ErrorCode.InternalError, "Reddit client not initialized")
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
console.log(`[Tool] Getting info for r/${subreddit_name}`)
|
|
15
|
+
const subreddit = await client.getSubredditInfo(subreddit_name)
|
|
16
|
+
const formattedSubreddit = formatSubredditInfo(subreddit)
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: "text",
|
|
22
|
+
text: `
|
|
23
|
+
# Subreddit Information: r/${formattedSubreddit.name}
|
|
24
|
+
|
|
25
|
+
## Overview
|
|
26
|
+
- Name: r/${formattedSubreddit.name}
|
|
27
|
+
- Title: ${formattedSubreddit.title}
|
|
28
|
+
- Subscribers: ${formattedSubreddit.stats.subscribers.toLocaleString()}
|
|
29
|
+
- Active Users: ${
|
|
30
|
+
typeof formattedSubreddit.stats.activeUsers === "number"
|
|
31
|
+
? formattedSubreddit.stats.activeUsers.toLocaleString()
|
|
32
|
+
: formattedSubreddit.stats.activeUsers
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
## Description
|
|
36
|
+
${formattedSubreddit.description.short}
|
|
37
|
+
|
|
38
|
+
## Detailed Description
|
|
39
|
+
${formattedSubreddit.description.full}
|
|
40
|
+
|
|
41
|
+
## Metadata
|
|
42
|
+
- Created: ${formattedSubreddit.metadata.created}
|
|
43
|
+
- Flags: ${formattedSubreddit.metadata.flags.join(", ")}
|
|
44
|
+
|
|
45
|
+
## Links
|
|
46
|
+
- Subreddit: ${formattedSubreddit.links.subreddit}
|
|
47
|
+
- Wiki: ${formattedSubreddit.links.wiki}
|
|
48
|
+
|
|
49
|
+
## Community Analysis
|
|
50
|
+
- ${formattedSubreddit.communityAnalysis.replace(/\n - /g, "\n- ")}
|
|
51
|
+
|
|
52
|
+
## Engagement Tips
|
|
53
|
+
- ${formattedSubreddit.engagementTips.replace(/\n - /g, "\n- ")}
|
|
54
|
+
`,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error(`[Error] Error getting subreddit info: ${error}`)
|
|
60
|
+
throw new McpError(ErrorCode.InternalError, `Failed to fetch subreddit data: ${error}`)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function getTrendingSubreddits() {
|
|
65
|
+
const client = getRedditClient()
|
|
66
|
+
|
|
67
|
+
if (!client) {
|
|
68
|
+
throw new McpError(ErrorCode.InternalError, "Reddit client not initialized")
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
console.log("[Tool] Getting trending subreddits")
|
|
73
|
+
const trendingSubreddits = await client.getTrendingSubreddits()
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
content: [
|
|
77
|
+
{
|
|
78
|
+
type: "text",
|
|
79
|
+
text: `
|
|
80
|
+
# Trending Subreddits
|
|
81
|
+
|
|
82
|
+
${trendingSubreddits.map((subreddit, index) => `${index + 1}. r/${subreddit}`).join("\n")}
|
|
83
|
+
`,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error(`[Error] Error getting trending subreddits: ${error}`)
|
|
89
|
+
throw new McpError(ErrorCode.InternalError, `Failed to fetch trending subreddits: ${error}`)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { getRedditClient } from "../client/reddit-client"
|
|
2
|
+
import { formatUserInfo } from "../utils/formatters"
|
|
3
|
+
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"
|
|
4
|
+
|
|
5
|
+
export async function getUserInfo(params: { username: string }) {
|
|
6
|
+
const { username } = params
|
|
7
|
+
const client = getRedditClient()
|
|
8
|
+
|
|
9
|
+
if (!client) {
|
|
10
|
+
throw new McpError(ErrorCode.InternalError, "Reddit client not initialized")
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
console.log(`[Tool] Getting info for u/${username}`)
|
|
15
|
+
const user = await client.getUser(username)
|
|
16
|
+
const formattedUser = formatUserInfo(user)
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: "text",
|
|
22
|
+
text: `
|
|
23
|
+
# User Information: u/${formattedUser.username}
|
|
24
|
+
|
|
25
|
+
## Profile Overview
|
|
26
|
+
- Username: u/${formattedUser.username}
|
|
27
|
+
- Karma:
|
|
28
|
+
- Comment Karma: ${formattedUser.karma.commentKarma.toLocaleString()}
|
|
29
|
+
- Post Karma: ${formattedUser.karma.postKarma.toLocaleString()}
|
|
30
|
+
- Total Karma: ${formattedUser.karma.totalKarma.toLocaleString()}
|
|
31
|
+
- Account Status: ${formattedUser.accountStatus.join(", ")}
|
|
32
|
+
- Account Created: ${formattedUser.accountCreated}
|
|
33
|
+
- Profile URL: ${formattedUser.profileUrl}
|
|
34
|
+
|
|
35
|
+
## Activity Analysis
|
|
36
|
+
- ${formattedUser.activityAnalysis.replace(/\n - /g, "\n- ")}
|
|
37
|
+
|
|
38
|
+
## Recommendations
|
|
39
|
+
- ${formattedUser.recommendations.replace(/\n - /g, "\n- ")}
|
|
40
|
+
`,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
}
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error(`[Error] Error getting user info: ${error}`)
|
|
46
|
+
throw new McpError(ErrorCode.InternalError, `Failed to fetch user data: ${error}`)
|
|
47
|
+
}
|
|
48
|
+
}
|