reddit-mcp-server 1.2.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,10 +1,7 @@
1
- const require_chunk = require('./chunk-kSYXY2_d.js');
2
- let fastmcp = require("fastmcp");
3
- let zod = require("zod");
4
- let crypto = require("crypto");
5
- crypto = require_chunk.__toESM(crypto);
6
- let dotenv = require("dotenv");
7
- dotenv = require_chunk.__toESM(dotenv);
1
+ import crypto from "crypto";
2
+ import dotenv from "dotenv";
3
+ import { FastMCP } from "fastmcp";
4
+ import { z } from "zod";
8
5
 
9
6
  //#region src/client/reddit-client.ts
10
7
  var RedditClient = class {
@@ -87,8 +84,8 @@ var RedditClient = class {
87
84
  if (this.accessToken && now < this.tokenExpiry) return;
88
85
  const authUrl = "https://www.reddit.com/api/v1/access_token";
89
86
  const authData = new URLSearchParams();
90
- const username = this.username;
91
- const password = this.password;
87
+ const { username } = this;
88
+ const { password } = this;
92
89
  if (!!(username && password)) {
93
90
  authData.append("grant_type", "password");
94
91
  authData.append("username", username);
@@ -114,7 +111,7 @@ var RedditClient = class {
114
111
  this.authenticated = true;
115
112
  } catch (error) {
116
113
  if (error instanceof Error) throw error;
117
- throw new Error("Failed to authenticate with Reddit API");
114
+ throw new Error("Failed to authenticate with Reddit API", { cause: error });
118
115
  }
119
116
  }
120
117
  async checkAuthentication() {
@@ -143,7 +140,7 @@ var RedditClient = class {
143
140
  this.lastWriteTime = Date.now();
144
141
  }
145
142
  hashContent(content) {
146
- return crypto.default.createHash("md5").update(content.trim().toLowerCase()).digest("hex");
143
+ return crypto.createHash("sha256").update(content.trim().toLowerCase()).digest("hex");
147
144
  }
148
145
  checkDuplicateContent(content) {
149
146
  if (!this.safeMode.enabled || !this.safeMode.duplicateCheck) return;
@@ -159,7 +156,7 @@ var RedditClient = class {
159
156
  try {
160
157
  const response = await this.makeRequest(`/user/${username}/about.json`);
161
158
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
162
- const data = (await response.json()).data;
159
+ const { data } = await response.json();
163
160
  return {
164
161
  name: data.name,
165
162
  id: data.id,
@@ -180,7 +177,7 @@ var RedditClient = class {
180
177
  try {
181
178
  const response = await this.makeRequest(`/r/${subredditName}/about.json`);
182
179
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
183
- const data = (await response.json()).data;
180
+ const { data } = await response.json();
184
181
  return {
185
182
  displayName: data.display_name,
186
183
  title: data.title,
@@ -293,30 +290,18 @@ var RedditClient = class {
293
290
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
294
291
  body: params.toString()
295
292
  });
296
- if (!response.ok) {
297
- const errorText = await response.text();
298
- console.error(`[Reddit API] Create post failed: ${response.status} ${response.statusText}`);
299
- console.error(`[Reddit API] Error response: ${errorText}`);
300
- throw new Error(`HTTP ${response.status}: ${errorText}`);
301
- }
293
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
302
294
  const json = await response.json();
303
- console.error(`[Reddit API] Create post response:`, JSON.stringify(json, null, 2));
304
295
  if (((_json$json = json.json) === null || _json$json === void 0 ? void 0 : _json$json.errors) && json.json.errors.length > 0) {
305
- const errors = json.json.errors.map((e) => e.join(": ")).join(", ");
306
- console.error(`[Reddit API] Post creation errors: ${errors}`);
296
+ const errors = json.json.errors.map((e) => e[1] || e[0]).join(", ");
307
297
  throw new Error(`Reddit API errors: ${errors}`);
308
298
  }
309
299
  const postId = ((_json$json2 = json.json) === null || _json$json2 === void 0 || (_json$json2 = _json$json2.data) === null || _json$json2 === void 0 ? void 0 : _json$json2.id) || ((_json$json3 = json.json) === null || _json$json3 === void 0 || (_json$json3 = _json$json3.data) === null || _json$json3 === void 0 || (_json$json3 = _json$json3.name) === null || _json$json3 === void 0 ? void 0 : _json$json3.replace("t3_", ""));
310
- if (!postId) {
311
- console.error(`[Reddit API] No post ID in response`);
312
- throw new Error("No post ID returned from Reddit");
313
- }
314
- console.error(`[Reddit API] Post created with ID: ${postId}`);
300
+ if (!postId) throw new Error("No post ID returned from Reddit");
315
301
  return await this.getPost(postId, subreddit);
316
302
  } catch (error) {
317
- console.error(`[Reddit API] Create post exception:`, error);
318
- if (error instanceof Error && error.message.includes("HTTP")) throw error;
319
- throw new Error(`Failed to create post in ${subreddit}: ${error instanceof Error ? error.message : String(error)}`);
303
+ if (error instanceof Error) throw error;
304
+ throw new Error(`Failed to create post in ${subreddit}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
320
305
  }
321
306
  }
322
307
  async checkPostExists(postId) {
@@ -334,9 +319,10 @@ var RedditClient = class {
334
319
  this.checkDuplicateContent(content);
335
320
  try {
336
321
  var _json$json4, _json$json5;
337
- if (!await this.checkPostExists(postId)) throw new Error(`Post with ID ${postId} does not exist or is not accessible`);
322
+ const fullThingId = postId.startsWith("t3_") || postId.startsWith("t1_") ? postId : `t3_${postId}`;
323
+ if (!postId.startsWith("t1_") && !await this.checkPostExists(postId.replace(/^t3_/, ""))) throw new Error(`Post with ID ${postId} does not exist or is not accessible`);
338
324
  const params = new URLSearchParams();
339
- params.append("thing_id", `t3_${postId}`);
325
+ params.append("thing_id", fullThingId);
340
326
  params.append("text", content);
341
327
  params.append("api_type", "json");
342
328
  const response = await this.makeRequest("/api/comment", {
@@ -344,14 +330,8 @@ var RedditClient = class {
344
330
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
345
331
  body: params.toString()
346
332
  });
347
- if (!response.ok) {
348
- const errorText = await response.text();
349
- console.error(`[Reddit API] Reply to post failed: ${response.status} ${response.statusText}`);
350
- console.error(`[Reddit API] Error response: ${errorText}`);
351
- throw new Error(`HTTP ${response.status}: ${errorText}`);
352
- }
333
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
353
334
  const json = await response.json();
354
- console.error(`[Reddit API] Reply response:`, JSON.stringify(json, null, 2));
355
335
  if (((_json$json4 = json.json) === null || _json$json4 === void 0 || (_json$json4 = _json$json4.data) === null || _json$json4 === void 0 ? void 0 : _json$json4.things) && json.json.data.things.length > 0) {
356
336
  const commentData = json.json.data.things[0].data;
357
337
  const author = this.username ?? "[unknown]";
@@ -369,17 +349,12 @@ var RedditClient = class {
369
349
  permalink: commentData.permalink
370
350
  };
371
351
  } else if (((_json$json5 = json.json) === null || _json$json5 === void 0 ? void 0 : _json$json5.errors) && json.json.errors.length > 0) {
372
- const errors = json.json.errors.map((e) => e.join(": ")).join(", ");
373
- console.error(`[Reddit API] Reply errors: ${errors}`);
352
+ const errors = json.json.errors.map((e) => e[1] || e[0]).join(", ");
374
353
  throw new Error(`Reddit API errors: ${errors}`);
375
- } else {
376
- console.error(`[Reddit API] Unexpected reply response format`);
377
- throw new Error("Failed to parse reply response");
378
- }
354
+ } else throw new Error("Failed to parse reply response");
379
355
  } catch (error) {
380
- console.error(`[Reddit API] Reply to post exception:`, error);
381
- if (error instanceof Error && error.message.includes("HTTP")) throw error;
382
- throw new Error(`Failed to reply to post ${postId}: ${error instanceof Error ? error.message : String(error)}`);
356
+ if (error instanceof Error) throw error;
357
+ throw new Error(`Failed to reply to post ${postId}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
383
358
  }
384
359
  }
385
360
  async deletePost(thingId) {
@@ -404,7 +379,7 @@ var RedditClient = class {
404
379
  } catch (error) {
405
380
  console.error(`[Reddit API] Delete exception:`, error);
406
381
  if (error instanceof Error && error.message.includes("HTTP")) throw error;
407
- throw new Error(`Failed to delete content ${thingId}: ${error instanceof Error ? error.message : String(error)}`);
382
+ throw new Error(`Failed to delete content ${thingId}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
408
383
  }
409
384
  }
410
385
  async deleteComment(thingId) {
@@ -427,25 +402,16 @@ var RedditClient = class {
427
402
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
428
403
  body: params.toString()
429
404
  });
430
- if (!response.ok) {
431
- const errorText = await response.text();
432
- console.error(`[Reddit API] Edit failed: ${response.status} ${response.statusText}`);
433
- console.error(`[Reddit API] Error response: ${errorText}`);
434
- throw new Error(`HTTP ${response.status}: ${errorText}`);
435
- }
405
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
436
406
  const json = await response.json();
437
- console.error(`[Reddit API] Edit response:`, JSON.stringify(json, null, 2));
438
407
  if (((_json$json6 = json.json) === null || _json$json6 === void 0 ? void 0 : _json$json6.errors) && json.json.errors.length > 0) {
439
- const errors = json.json.errors.map((e) => e.join(": ")).join(", ");
440
- console.error(`[Reddit API] Edit errors: ${errors}`);
408
+ const errors = json.json.errors.map((e) => e[1] || e[0]).join(", ");
441
409
  throw new Error(`Reddit API errors: ${errors}`);
442
410
  }
443
- console.error(`[Reddit API] Successfully edited ${fullThingId}`);
444
411
  return true;
445
412
  } catch (error) {
446
- console.error(`[Reddit API] Edit exception:`, error);
447
- if (error instanceof Error && error.message.includes("HTTP")) throw error;
448
- throw new Error(`Failed to edit content ${thingId}: ${error instanceof Error ? error.message : String(error)}`);
413
+ if (error instanceof Error) throw error;
414
+ throw new Error(`Failed to edit content ${thingId}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
449
415
  }
450
416
  }
451
417
  async editComment(thingId, newText) {
@@ -540,7 +506,7 @@ var RedditClient = class {
540
506
  depth,
541
507
  parentId: item.data.parent_id
542
508
  });
543
- const replies = item.data.replies;
509
+ const { replies } = item.data;
544
510
  if (replies && typeof replies !== "string" && ((_replies$data = replies.data) === null || _replies$data === void 0 ? void 0 : _replies$data.children)) parseComments(replies.data.children, depth + 1);
545
511
  }
546
512
  };
@@ -734,7 +700,7 @@ function formatPostInfo(post) {
734
700
  return {
735
701
  title: post.title,
736
702
  type: contentType,
737
- content: content.length > 300 ? content.substring(0, 297) + "..." : content,
703
+ content: content.length > 300 ? `${content.substring(0, 297)}...` : content,
738
704
  author: post.author,
739
705
  subreddit: post.subreddit,
740
706
  stats: {
@@ -769,7 +735,7 @@ function formatSubredditInfo(subreddit) {
769
735
  },
770
736
  description: {
771
737
  short: subreddit.publicDescription,
772
- full: subreddit.description.length > 300 ? subreddit.description.substring(0, 297) + "..." : subreddit.description
738
+ full: subreddit.description.length > 300 ? `${subreddit.description.substring(0, 297)}...` : subreddit.description
773
739
  },
774
740
  metadata: {
775
741
  created: formatTimestamp(subreddit.createdUtc),
@@ -786,13 +752,14 @@ function formatSubredditInfo(subreddit) {
786
752
 
787
753
  //#endregion
788
754
  //#region src/index.ts
789
- dotenv.default.config();
755
+ dotenv.config();
756
+ const VERSION = "1.3.2";
790
757
  function validateUserAgent(userAgent, username) {
791
758
  if (!/^[\w-]+:[\w-]+:[\d.]+ \(by \/u\/\w+\)$/.test(userAgent)) {
792
759
  console.error("[Warning] User-Agent does not follow Reddit's recommended format");
793
760
  console.error("[Warning] Recommended: 'platform:app_id:version (by /u/username)'");
794
761
  console.error("[Warning] Non-standard User-Agents may increase ban risk");
795
- if (username) console.error(`[Warning] Consider using: 'typescript:reddit-mcp-server:1.2.0 (by /u/${username})'`);
762
+ if (username) console.error(`[Warning] Consider using: 'typescript:reddit-mcp-server:${VERSION} (by /u/${username})'`);
796
763
  }
797
764
  }
798
765
  function buildUserAgent(customAgent, username) {
@@ -801,12 +768,12 @@ function buildUserAgent(customAgent, username) {
801
768
  return customAgent;
802
769
  }
803
770
  if (username) {
804
- const autoAgent = `typescript:reddit-mcp-server:1.2.0 (by /u/${username})`;
771
+ const autoAgent = `typescript:reddit-mcp-server:${VERSION} (by /u/${username})`;
805
772
  console.error(`[Setup] Auto-generated User-Agent: ${autoAgent}`);
806
773
  return autoAgent;
807
774
  }
808
- const fallbackAgent = "RedditMCPServer/1.2.0";
809
- validateUserAgent(fallbackAgent);
775
+ const fallbackAgent = `typescript:reddit-mcp-server:${VERSION} (by /u/anonymous)`;
776
+ console.error("[Setup] No REDDIT_USERNAME set — using anonymous User-Agent. Set REDDIT_USERNAME for a personalized agent.");
810
777
  return fallbackAgent;
811
778
  }
812
779
  function buildSafeModeConfig(safeMode) {
@@ -913,9 +880,11 @@ async function setupRedditClient() {
913
880
  process.exit(1);
914
881
  }
915
882
  }
916
- const server = new fastmcp.FastMCP({
883
+ const oauthToken = process.env.OAUTH_TOKEN || crypto.randomBytes(32).toString("hex");
884
+ if (process.env.OAUTH_ENABLED === "true" && !process.env.OAUTH_TOKEN) console.error(`[Auth] Generated OAuth token: ${oauthToken}`);
885
+ const server = new FastMCP({
917
886
  name: "reddit-mcp-server",
918
- version: "1.2.0",
887
+ version: VERSION,
919
888
  instructions: `A comprehensive Reddit MCP server that provides tools for interacting with Reddit API.
920
889
 
921
890
  Available capabilities:
@@ -930,23 +899,16 @@ Available capabilities:
930
899
  For write operations (posting, replying, editing, deleting), ensure REDDIT_USERNAME and REDDIT_PASSWORD are configured.`,
931
900
  ...process.env.OAUTH_ENABLED === "true" && { authenticate: async (request) => {
932
901
  const authHeader = request.headers.authorization;
933
- const expectedToken = process.env.OAUTH_TOKEN;
934
- if (!expectedToken) {
935
- const token = Array.from({ length: 32 }, () => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".charAt(Math.floor(Math.random() * 62))).join("");
936
- console.log(`[Auth] Generated OAuth token: ${token}`);
937
- throw new Response(JSON.stringify({
938
- error: "No OAuth token configured",
939
- generatedToken: token
940
- }), {
941
- status: 401,
942
- headers: { "Content-Type": "application/json" }
943
- });
944
- }
945
902
  if (!(authHeader === null || authHeader === void 0 ? void 0 : authHeader.startsWith("Bearer "))) throw new Response(null, {
946
903
  status: 401,
947
904
  statusText: "Missing or invalid Authorization header"
948
905
  });
949
- if (authHeader.slice(7) !== expectedToken) throw new Response(null, {
906
+ const token = authHeader.slice(7);
907
+ const tokenBuffer = Buffer.from(token);
908
+ const expectedBuffer = Buffer.from(oauthToken);
909
+ const tokenHash = crypto.createHash("sha256").update(tokenBuffer).digest();
910
+ const expectedHash = crypto.createHash("sha256").update(expectedBuffer).digest();
911
+ if (!crypto.timingSafeEqual(tokenHash, expectedHash)) throw new Response(null, {
950
912
  status: 403,
951
913
  statusText: "Invalid token"
952
914
  });
@@ -956,16 +918,16 @@ For write operations (posting, replying, editing, deleting), ensure REDDIT_USERN
956
918
  server.addTool({
957
919
  name: "test_reddit_mcp_server",
958
920
  description: "Test the Reddit MCP Server connection and configuration",
959
- parameters: zod.z.object({}),
921
+ parameters: z.object({}),
960
922
  execute: async () => {
961
923
  const client = getRedditClient();
962
924
  const hasAuth = client ? "✓" : "✗";
963
925
  const hasWriteAccess = process.env.REDDIT_USERNAME && process.env.REDDIT_PASSWORD ? "✓" : "✗";
964
926
  return `Reddit MCP Server Status:
965
927
  - Server: ✓ Running
966
- - Reddit Client: ${hasAuth} ${client ? "Initialized" : "Not initialized"}
928
+ - Reddit Client: ${hasAuth} ${client ? "Initialized" : "Not initialized"}
967
929
  - Write Access: ${hasWriteAccess} ${hasWriteAccess === "✓" ? "Available" : "Read-only mode"}
968
- - Version: 1.2.0
930
+ - Version: ${VERSION}
969
931
 
970
932
  Ready to handle Reddit API requests!`;
971
933
  }
@@ -973,7 +935,7 @@ Ready to handle Reddit API requests!`;
973
935
  server.addTool({
974
936
  name: "get_user_info",
975
937
  description: "Get detailed information about a Reddit user including karma, account status, and activity analysis",
976
- parameters: zod.z.object({ username: zod.z.string().describe("The Reddit username (without u/ prefix)") }),
938
+ parameters: z.object({ username: z.string().describe("The Reddit username (without u/ prefix)") }),
977
939
  execute: async (args) => {
978
940
  const client = getRedditClient();
979
941
  if (!client) throw new Error("Reddit client not initialized");
@@ -991,7 +953,7 @@ server.addTool({
991
953
  - Profile URL: ${formattedUser.profileUrl}
992
954
 
993
955
  ## Activity Analysis
994
- - ${formattedUser.activityAnalysis.replace(/\n - /g, "\n- ")}
956
+ - ${formattedUser.activityAnalysis.replace(/\n {2}- /g, "\n- ")}
995
957
 
996
958
  ## Recommendations
997
959
  - ${formattedUser.recommendations.replace(/\n {2}- /g, "\n- ")}`;
@@ -1000,14 +962,14 @@ server.addTool({
1000
962
  server.addTool({
1001
963
  name: "get_user_posts",
1002
964
  description: "Get recent posts by a Reddit user with sorting and filtering options",
1003
- parameters: zod.z.object({
1004
- username: zod.z.string().describe("The Reddit username (without u/ prefix)"),
1005
- sort: zod.z.enum([
965
+ parameters: z.object({
966
+ username: z.string().describe("The Reddit username (without u/ prefix)"),
967
+ sort: z.enum([
1006
968
  "new",
1007
969
  "hot",
1008
970
  "top"
1009
971
  ]).default("new").describe("Sort order for posts"),
1010
- time_filter: zod.z.enum([
972
+ time_filter: z.enum([
1011
973
  "hour",
1012
974
  "day",
1013
975
  "week",
@@ -1015,7 +977,7 @@ server.addTool({
1015
977
  "year",
1016
978
  "all"
1017
979
  ]).default("all").describe("Time filter for top posts"),
1018
- limit: zod.z.number().min(1).max(100).default(10).describe("Number of posts to retrieve")
980
+ limit: z.number().min(1).max(100).default(10).describe("Number of posts to retrieve")
1019
981
  }),
1020
982
  execute: async (args) => {
1021
983
  const client = getRedditClient();
@@ -1043,14 +1005,14 @@ ${postSummaries}`;
1043
1005
  server.addTool({
1044
1006
  name: "get_user_comments",
1045
1007
  description: "Get recent comments by a Reddit user with sorting and filtering options",
1046
- parameters: zod.z.object({
1047
- username: zod.z.string().describe("The Reddit username (without u/ prefix)"),
1048
- sort: zod.z.enum([
1008
+ parameters: z.object({
1009
+ username: z.string().describe("The Reddit username (without u/ prefix)"),
1010
+ sort: z.enum([
1049
1011
  "new",
1050
1012
  "hot",
1051
1013
  "top"
1052
1014
  ]).default("new").describe("Sort order for comments"),
1053
- time_filter: zod.z.enum([
1015
+ time_filter: z.enum([
1054
1016
  "hour",
1055
1017
  "day",
1056
1018
  "week",
@@ -1058,7 +1020,7 @@ server.addTool({
1058
1020
  "year",
1059
1021
  "all"
1060
1022
  ]).default("all").describe("Time filter for top comments"),
1061
- limit: zod.z.number().min(1).max(100).default(10).describe("Number of comments to retrieve")
1023
+ limit: z.number().min(1).max(100).default(10).describe("Number of comments to retrieve")
1062
1024
  }),
1063
1025
  execute: async (args) => {
1064
1026
  const client = getRedditClient();
@@ -1070,7 +1032,7 @@ server.addTool({
1070
1032
  });
1071
1033
  if (comments.length === 0) return `No comments found for u/${args.username} with the specified filters.`;
1072
1034
  const commentSummaries = comments.map((comment, index) => {
1073
- const truncatedBody = comment.body.length > 300 ? comment.body.substring(0, 300) + "..." : comment.body;
1035
+ const truncatedBody = comment.body.length > 300 ? `${comment.body.substring(0, 300)}...` : comment.body;
1074
1036
  const flags = [...comment.edited ? ["*(edited)*"] : [], ...comment.isSubmitter ? ["**OP**"] : []];
1075
1037
  return `### ${index + 1}. Comment ${flags.join(" ")}
1076
1038
  In r/${comment.subreddit} on "${comment.submissionTitle}"
@@ -1089,9 +1051,9 @@ ${commentSummaries}`;
1089
1051
  server.addTool({
1090
1052
  name: "get_reddit_post",
1091
1053
  description: "Get detailed information about a specific Reddit post including content, stats, and engagement analysis",
1092
- parameters: zod.z.object({
1093
- subreddit: zod.z.string().describe("The subreddit name (without r/ prefix)"),
1094
- post_id: zod.z.string().describe("The Reddit post ID")
1054
+ parameters: z.object({
1055
+ subreddit: z.string().describe("The subreddit name (without r/ prefix)"),
1056
+ post_id: z.string().describe("The Reddit post ID")
1095
1057
  }),
1096
1058
  execute: async (args) => {
1097
1059
  const client = getRedditClient();
@@ -1122,7 +1084,7 @@ ${formattedPost.content}
1122
1084
  - Short Link: ${formattedPost.links.shortLink}
1123
1085
 
1124
1086
  ## Engagement Analysis
1125
- - ${formattedPost.engagementAnalysis.replace(/\n - /g, "\n- ")}
1087
+ - ${formattedPost.engagementAnalysis.replace(/\n {2}- /g, "\n- ")}
1126
1088
 
1127
1089
  ## Best Time to Engage
1128
1090
  ${formattedPost.bestTimeToEngage}`;
@@ -1131,9 +1093,9 @@ ${formattedPost.bestTimeToEngage}`;
1131
1093
  server.addTool({
1132
1094
  name: "get_top_posts",
1133
1095
  description: "Get top posts from a subreddit or from the Reddit home feed",
1134
- parameters: zod.z.object({
1135
- subreddit: zod.z.string().optional().describe("The subreddit name (without r/ prefix). Leave empty for home feed"),
1136
- time_filter: zod.z.enum([
1096
+ parameters: z.object({
1097
+ subreddit: z.string().optional().describe("The subreddit name (without r/ prefix). Leave empty for home feed"),
1098
+ time_filter: z.enum([
1137
1099
  "hour",
1138
1100
  "day",
1139
1101
  "week",
@@ -1141,7 +1103,7 @@ server.addTool({
1141
1103
  "year",
1142
1104
  "all"
1143
1105
  ]).default("week").describe("Time period for top posts"),
1144
- limit: zod.z.number().min(1).max(100).default(10).describe("Number of posts to retrieve")
1106
+ limit: z.number().min(1).max(100).default(10).describe("Number of posts to retrieve")
1145
1107
  }),
1146
1108
  execute: async (args) => {
1147
1109
  const client = getRedditClient();
@@ -1162,7 +1124,7 @@ ${postSummaries}`;
1162
1124
  server.addTool({
1163
1125
  name: "get_subreddit_info",
1164
1126
  description: "Get detailed information about a subreddit including description, stats, and community analysis",
1165
- parameters: zod.z.object({ subreddit_name: zod.z.string().describe("The subreddit name (without r/ prefix)") }),
1127
+ parameters: z.object({ subreddit_name: z.string().describe("The subreddit name (without r/ prefix)") }),
1166
1128
  execute: async (args) => {
1167
1129
  const client = getRedditClient();
1168
1130
  if (!client) throw new Error("Reddit client not initialized");
@@ -1190,16 +1152,16 @@ ${formattedSubreddit.description.full}
1190
1152
  - Wiki: ${formattedSubreddit.links.wiki}
1191
1153
 
1192
1154
  ## Community Analysis
1193
- - ${formattedSubreddit.communityAnalysis.replace(/\n - /g, "\n- ")}
1155
+ - ${formattedSubreddit.communityAnalysis.replace(/\n {2}- /g, "\n- ")}
1194
1156
 
1195
1157
  ## Engagement Tips
1196
- - ${formattedSubreddit.engagementTips.replace(/\n - /g, "\n- ")}`;
1158
+ - ${formattedSubreddit.engagementTips.replace(/\n {2}- /g, "\n- ")}`;
1197
1159
  }
1198
1160
  });
1199
1161
  server.addTool({
1200
1162
  name: "get_trending_subreddits",
1201
1163
  description: "Get a list of currently trending subreddits",
1202
- parameters: zod.z.object({}),
1164
+ parameters: z.object({}),
1203
1165
  execute: async () => {
1204
1166
  const client = getRedditClient();
1205
1167
  if (!client) throw new Error("Reddit client not initialized");
@@ -1211,17 +1173,17 @@ ${(await client.getTrendingSubreddits()).map((subreddit, index) => `${index + 1}
1211
1173
  server.addTool({
1212
1174
  name: "search_reddit",
1213
1175
  description: "Search Reddit for posts and content across subreddits",
1214
- parameters: zod.z.object({
1215
- query: zod.z.string().describe("Search query"),
1216
- subreddit: zod.z.string().optional().describe("Limit search to specific subreddit (without r/ prefix)"),
1217
- sort: zod.z.enum([
1176
+ parameters: z.object({
1177
+ query: z.string().describe("Search query"),
1178
+ subreddit: z.string().optional().describe("Limit search to specific subreddit (without r/ prefix)"),
1179
+ sort: z.enum([
1218
1180
  "relevance",
1219
1181
  "hot",
1220
1182
  "top",
1221
1183
  "new",
1222
1184
  "comments"
1223
1185
  ]).default("relevance").describe("Sort order"),
1224
- time_filter: zod.z.enum([
1186
+ time_filter: z.enum([
1225
1187
  "hour",
1226
1188
  "day",
1227
1189
  "week",
@@ -1229,8 +1191,8 @@ server.addTool({
1229
1191
  "year",
1230
1192
  "all"
1231
1193
  ]).default("all").describe("Time filter"),
1232
- limit: zod.z.number().min(1).max(100).default(10).describe("Number of results"),
1233
- type: zod.z.enum([
1194
+ limit: z.number().min(1).max(100).default(10).describe("Number of results"),
1195
+ type: z.enum([
1234
1196
  "link",
1235
1197
  "sr",
1236
1198
  "user"
@@ -1248,8 +1210,8 @@ server.addTool({
1248
1210
  type: args.type
1249
1211
  });
1250
1212
  if (posts.length === 0) {
1251
- const searchLocation$1 = args.subreddit ? ` in r/${args.subreddit}` : "";
1252
- return `No results found for "${args.query}"${searchLocation$1}.`;
1213
+ const searchLocation = args.subreddit ? ` in r/${args.subreddit}` : "";
1214
+ return `No results found for "${args.query}"${searchLocation}.`;
1253
1215
  }
1254
1216
  const searchResults = posts.map((post, index) => {
1255
1217
  const flags = [...post.over18 ? ["**NSFW**"] : [], ...post.spoiler ? ["**Spoiler**"] : []];
@@ -1272,11 +1234,11 @@ ${searchResults}`;
1272
1234
  server.addTool({
1273
1235
  name: "create_post",
1274
1236
  description: "Create a new post in a subreddit (requires REDDIT_USERNAME and REDDIT_PASSWORD). WARNING: Rapid posting or duplicate content may trigger Reddit's spam detection and result in account bans. Consider enabling REDDIT_SAFE_MODE=standard for protection.",
1275
- parameters: zod.z.object({
1276
- subreddit: zod.z.string().describe("The subreddit name (without r/ prefix)"),
1277
- title: zod.z.string().describe("The post title"),
1278
- content: zod.z.string().describe("The post content (text for self posts, URL for link posts)"),
1279
- is_self: zod.z.boolean().default(true).describe("Whether this is a self post (text) or link post")
1237
+ parameters: z.object({
1238
+ subreddit: z.string().describe("The subreddit name (without r/ prefix)"),
1239
+ title: z.string().describe("The post title"),
1240
+ content: z.string().describe("The post content (text for self posts, URL for link posts)"),
1241
+ is_self: z.boolean().default(true).describe("Whether this is a self post (text) or link post")
1280
1242
  }),
1281
1243
  execute: async (args) => {
1282
1244
  const client = getRedditClient();
@@ -1297,9 +1259,9 @@ Your post has been successfully submitted to r/${formattedPost.subreddit}.`;
1297
1259
  server.addTool({
1298
1260
  name: "reply_to_post",
1299
1261
  description: "Post a reply to an existing Reddit post or comment (requires REDDIT_USERNAME and REDDIT_PASSWORD). WARNING: Rapid commenting or duplicate content may trigger Reddit's spam detection. Enable REDDIT_SAFE_MODE=standard for rate limiting and duplicate detection.",
1300
- parameters: zod.z.object({
1301
- post_id: zod.z.string().describe("The Reddit post ID (thing_id, e.g., t3_xxxxx for posts, t1_xxxxx for comments)"),
1302
- content: zod.z.string().describe("The reply content")
1262
+ parameters: z.object({
1263
+ post_id: z.string().describe("The Reddit post ID (thing_id, e.g., t3_xxxxx for posts, t1_xxxxx for comments)"),
1264
+ content: z.string().describe("The reply content")
1303
1265
  }),
1304
1266
  execute: async (args) => {
1305
1267
  const client = getRedditClient();
@@ -1319,7 +1281,7 @@ Your reply has been successfully posted.`;
1319
1281
  server.addTool({
1320
1282
  name: "delete_post",
1321
1283
  description: "Delete your own Reddit post (requires REDDIT_USERNAME and REDDIT_PASSWORD). WARNING: This action is permanent and cannot be undone!",
1322
- parameters: zod.z.object({ thing_id: zod.z.string().describe("The full Reddit thing ID (e.g., 't3_abc123' for posts) or just the post ID (e.g., 'abc123'). The 't3_' prefix will be added automatically if missing.") }),
1284
+ parameters: z.object({ thing_id: z.string().describe("The full Reddit thing ID (e.g., 't3_abc123' for posts) or just the post ID (e.g., 'abc123'). The 't3_' prefix will be added automatically if missing.") }),
1323
1285
  execute: async (args) => {
1324
1286
  const client = getRedditClient();
1325
1287
  if (!client) throw new Error("Reddit client not initialized");
@@ -1335,7 +1297,7 @@ The post ${args.thing_id} has been permanently deleted from Reddit.
1335
1297
  server.addTool({
1336
1298
  name: "delete_comment",
1337
1299
  description: "Delete your own Reddit comment (requires REDDIT_USERNAME and REDDIT_PASSWORD). WARNING: This action is permanent and cannot be undone!",
1338
- parameters: zod.z.object({ thing_id: zod.z.string().describe("The full Reddit thing ID (e.g., 't1_abc123' for comments) or just the comment ID (e.g., 'abc123'). The 't1_' prefix will be added automatically if missing.") }),
1300
+ parameters: z.object({ thing_id: z.string().describe("The full Reddit thing ID (e.g., 't1_abc123' for comments) or just the comment ID (e.g., 'abc123'). The 't1_' prefix will be added automatically if missing.") }),
1339
1301
  execute: async (args) => {
1340
1302
  const client = getRedditClient();
1341
1303
  if (!client) throw new Error("Reddit client not initialized");
@@ -1351,9 +1313,9 @@ The comment ${args.thing_id} has been permanently deleted from Reddit.
1351
1313
  server.addTool({
1352
1314
  name: "edit_post",
1353
1315
  description: "Edit your own Reddit post (self-text posts only, requires REDDIT_USERNAME and REDDIT_PASSWORD). You can only edit the text content of self posts, not titles or link posts. WARNING: Rapid edits may trigger spam detection. Enable REDDIT_SAFE_MODE for protection.",
1354
- parameters: zod.z.object({
1355
- thing_id: zod.z.string().describe("The full Reddit thing ID (e.g., 't3_abc123' for posts) or just the post ID (e.g., 'abc123'). The 't3_' prefix will be added automatically if missing."),
1356
- new_text: zod.z.string().describe("The new text content for the post. Supports Reddit markdown formatting.")
1316
+ parameters: z.object({
1317
+ thing_id: z.string().describe("The full Reddit thing ID (e.g., 't3_abc123' for posts) or just the post ID (e.g., 'abc123'). The 't3_' prefix will be added automatically if missing."),
1318
+ new_text: z.string().describe("The new text content for the post. Supports Reddit markdown formatting.")
1357
1319
  }),
1358
1320
  execute: async (args) => {
1359
1321
  const client = getRedditClient();
@@ -1374,9 +1336,9 @@ The post ${args.thing_id} has been updated with your new content.
1374
1336
  server.addTool({
1375
1337
  name: "edit_comment",
1376
1338
  description: "Edit your own Reddit comment (requires REDDIT_USERNAME and REDDIT_PASSWORD). Update the text content of a comment you previously posted. WARNING: Rapid edits may trigger spam detection. Enable REDDIT_SAFE_MODE for protection.",
1377
- parameters: zod.z.object({
1378
- thing_id: zod.z.string().describe("The full Reddit thing ID (e.g., 't1_abc123' for comments) or just the comment ID (e.g., 'abc123'). The 't1_' prefix will be added automatically if missing."),
1379
- new_text: zod.z.string().describe("The new text content for the comment. Supports Reddit markdown formatting.")
1339
+ parameters: z.object({
1340
+ thing_id: z.string().describe("The full Reddit thing ID (e.g., 't1_abc123' for comments) or just the comment ID (e.g., 'abc123'). The 't1_' prefix will be added automatically if missing."),
1341
+ new_text: z.string().describe("The new text content for the comment. Supports Reddit markdown formatting.")
1380
1342
  }),
1381
1343
  execute: async (args) => {
1382
1344
  const client = getRedditClient();
@@ -1393,10 +1355,10 @@ The comment ${args.thing_id} has been updated with your new content.
1393
1355
  server.addTool({
1394
1356
  name: "get_post_comments",
1395
1357
  description: "Get comments from a specific Reddit post",
1396
- parameters: zod.z.object({
1397
- post_id: zod.z.string().describe("The Reddit post ID"),
1398
- subreddit: zod.z.string().describe("The subreddit name (without r/ prefix)"),
1399
- sort: zod.z.enum([
1358
+ parameters: z.object({
1359
+ post_id: z.string().describe("The Reddit post ID"),
1360
+ subreddit: z.string().describe("The subreddit name (without r/ prefix)"),
1361
+ sort: z.enum([
1400
1362
  "best",
1401
1363
  "top",
1402
1364
  "new",
@@ -1404,7 +1366,7 @@ server.addTool({
1404
1366
  "old",
1405
1367
  "qa"
1406
1368
  ]).default("best").describe("Comment sort order"),
1407
- limit: zod.z.number().min(1).max(500).default(100).describe("Maximum number of comments to retrieve")
1369
+ limit: z.number().min(1).max(500).default(100).describe("Maximum number of comments to retrieve")
1408
1370
  }),
1409
1371
  execute: async (args) => {
1410
1372
  const client = getRedditClient();
@@ -1414,8 +1376,8 @@ server.addTool({
1414
1376
  sort: args.sort,
1415
1377
  limit: args.limit
1416
1378
  });
1417
- const post = data.post;
1418
- const comments = data.comments;
1379
+ const { post } = data;
1380
+ const { comments } = data;
1419
1381
  let response = `# Comments for: ${post.title}
1420
1382
 
1421
1383
  **Post by u/${post.author} in r/${post.subreddit}**
@@ -1448,7 +1410,7 @@ async function main() {
1448
1410
  await setupRedditClient();
1449
1411
  const useHttp = process.env.TRANSPORT_TYPE === "httpStream" || process.env.TRANSPORT_TYPE === "http";
1450
1412
  const port = parseInt(process.env.PORT || "3000");
1451
- const host = process.env.HOST || "0.0.0.0";
1413
+ const host = process.env.HOST || "127.0.0.1";
1452
1414
  if (useHttp) {
1453
1415
  console.error(`[Setup] Starting HTTP server on ${host}:${port}`);
1454
1416
  await server.start({
@@ -1481,4 +1443,5 @@ process.on("SIGTERM", async () => {
1481
1443
  main().catch(console.error);
1482
1444
 
1483
1445
  //#endregion
1446
+ export { };
1484
1447
  //# sourceMappingURL=index.js.map