reddit-mcp-server 1.2.1 → 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/bin.js CHANGED
@@ -1,23 +1,14 @@
1
1
  #!/usr/bin/env node
2
- const require_chunk = require('./chunk-kSYXY2_d.js');
3
- let fs = require("fs");
4
- fs = require_chunk.__toESM(fs);
5
- let path = require("path");
6
- path = require_chunk.__toESM(path);
7
-
8
2
  //#region src/bin.ts
9
- const packageJsonPath = path.default.join(__dirname, "..", "package.json");
10
- const packageJson = JSON.parse(fs.default.readFileSync(packageJsonPath, "utf-8"));
11
3
  if (!process.env.TRANSPORT_TYPE) process.env.TRANSPORT_TYPE = "stdio";
12
4
  const args = process.argv.slice(2);
13
5
  if (args.includes("--version") || args.includes("-v")) {
14
- console.log(packageJson.version);
6
+ console.log("1.3.2");
15
7
  process.exit(0);
16
8
  }
17
- main().then();
18
9
  if (args.includes("--help") || args.includes("-h")) {
19
10
  console.log(`
20
- Reddit MCP Server v${packageJson.version}
11
+ Reddit MCP Server v1.3.2
21
12
 
22
13
  Usage: reddit-mcp-server [options]
23
14
 
@@ -37,8 +28,10 @@ For more information, visit: https://github.com/jordanburke/reddit-mcp-server
37
28
  process.exit(0);
38
29
  }
39
30
  async function main() {
40
- await Promise.resolve().then(() => require("./index.js"));
31
+ await import("./index.js");
41
32
  }
33
+ main().then();
42
34
 
43
35
  //#endregion
36
+ export { };
44
37
  //# sourceMappingURL=bin.js.map
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","names":[],"sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\n\n// Read package.json dynamically\nimport fs from \"fs\"\nimport path from \"path\"\n\ninterface PackageJson {\n version: string\n}\n\nconst packageJsonPath = path.join(__dirname, \"..\", \"package.json\")\nconst packageJson = JSON.parse(fs.readFileSync(packageJsonPath, \"utf-8\")) as PackageJson\n\n// Force stdio mode for CLI/npx usage (unless explicitly overridden)\nif (!process.env.TRANSPORT_TYPE) {\n process.env.TRANSPORT_TYPE = \"stdio\"\n}\n\n// Handle command line arguments BEFORE any other imports\nconst args = process.argv.slice(2)\n\nif (args.includes(\"--version\") || args.includes(\"-v\")) {\n console.log(packageJson.version)\n process.exit(0)\n}\n\n// Only import and start server if not showing version/help\nmain().then()\n\nif (args.includes(\"--help\") || args.includes(\"-h\")) {\n console.log(`\nReddit MCP Server v${packageJson.version}\n\nUsage: reddit-mcp-server [options]\n\nOptions:\n -v, --version Show version number\n -h, --help Show help\n\nEnvironment Variables:\n REDDIT_CLIENT_ID Reddit API client ID (required)\n REDDIT_CLIENT_SECRET Reddit API client secret (required)\n REDDIT_USERNAME Reddit username (optional, for write operations)\n REDDIT_PASSWORD Reddit password (optional, for write operations)\n REDDIT_USER_AGENT Custom user agent (optional)\n\nFor more information, visit: https://github.com/jordanburke/reddit-mcp-server\n`)\n process.exit(0)\n}\n\n// Import and start server if not showing version/help\nasync function main() {\n // Import and run the main function from the FastMCP server\n await import(\"./index.js\")\n // The index.js exports main() directly, so we just need to execute the file\n // The main() is already executed when the module is imported\n}\n"],"mappings":";;;;;;;;AAUA,MAAM,kBAAkB,aAAK,KAAK,WAAW,MAAM,eAAe;AAClE,MAAM,cAAc,KAAK,MAAM,WAAG,aAAa,iBAAiB,QAAQ,CAAC;AAGzE,IAAI,CAAC,QAAQ,IAAI,eACf,SAAQ,IAAI,iBAAiB;AAI/B,MAAM,OAAO,QAAQ,KAAK,MAAM,EAAE;AAElC,IAAI,KAAK,SAAS,YAAY,IAAI,KAAK,SAAS,KAAK,EAAE;AACrD,SAAQ,IAAI,YAAY,QAAQ;AAChC,SAAQ,KAAK,EAAE;;AAIjB,MAAM,CAAC,MAAM;AAEb,IAAI,KAAK,SAAS,SAAS,IAAI,KAAK,SAAS,KAAK,EAAE;AAClD,SAAQ,IAAI;qBACO,YAAY,QAAQ;;;;;;;;;;;;;;;;EAgBvC;AACA,SAAQ,KAAK,EAAE;;AAIjB,eAAe,OAAO;AAEpB,4CAAM"}
1
+ {"version":3,"file":"bin.js","names":[],"sources":["../src/bin.ts"],"sourcesContent":["#!/usr/bin/env node\n\ndeclare const __VERSION__: string\n\n// Force stdio mode for CLI/npx usage (unless explicitly overridden)\nif (!process.env.TRANSPORT_TYPE) {\n process.env.TRANSPORT_TYPE = \"stdio\"\n}\n\n// Handle command line arguments BEFORE any other imports\nconst args = process.argv.slice(2)\n\nif (args.includes(\"--version\") || args.includes(\"-v\")) {\n console.log(__VERSION__)\n process.exit(0)\n}\n\nif (args.includes(\"--help\") || args.includes(\"-h\")) {\n console.log(`\nReddit MCP Server v${__VERSION__}\n\nUsage: reddit-mcp-server [options]\n\nOptions:\n -v, --version Show version number\n -h, --help Show help\n\nEnvironment Variables:\n REDDIT_CLIENT_ID Reddit API client ID (required)\n REDDIT_CLIENT_SECRET Reddit API client secret (required)\n REDDIT_USERNAME Reddit username (optional, for write operations)\n REDDIT_PASSWORD Reddit password (optional, for write operations)\n REDDIT_USER_AGENT Custom user agent (optional)\n\nFor more information, visit: https://github.com/jordanburke/reddit-mcp-server\n`)\n process.exit(0)\n}\n\n// Import and start server if not showing version/help\nasync function main() {\n // Import and run the main function from the FastMCP server\n await import(\"./index.js\")\n // The index.js exports main() directly, so we just need to execute the file\n // The main() is already executed when the module is imported\n}\n\nmain().then()\n"],"mappings":";;AAKA,IAAI,CAAC,QAAQ,IAAI,eACf,SAAQ,IAAI,iBAAiB;AAI/B,MAAM,OAAO,QAAQ,KAAK,MAAM,EAAE;AAElC,IAAI,KAAK,SAAS,YAAY,IAAI,KAAK,SAAS,KAAK,EAAE;AACrD,SAAQ,YAAgB;AACxB,SAAQ,KAAK,EAAE;;AAGjB,IAAI,KAAK,SAAS,SAAS,IAAI,KAAK,SAAS,KAAK,EAAE;AAClD,SAAQ,IAAI;;;;;;;;;;;;;;;;;EAiBZ;AACA,SAAQ,KAAK,EAAE;;AAIjB,eAAe,OAAO;AAEpB,OAAM,OAAO;;AAKf,MAAM,CAAC,MAAM"}
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,8 +752,8 @@ function formatSubredditInfo(subreddit) {
786
752
 
787
753
  //#endregion
788
754
  //#region src/index.ts
789
- dotenv.default.config();
790
- const VERSION = "1.2.1";
755
+ dotenv.config();
756
+ const VERSION = "1.3.2";
791
757
  function validateUserAgent(userAgent, username) {
792
758
  if (!/^[\w-]+:[\w-]+:[\d.]+ \(by \/u\/\w+\)$/.test(userAgent)) {
793
759
  console.error("[Warning] User-Agent does not follow Reddit's recommended format");
@@ -806,8 +772,8 @@ function buildUserAgent(customAgent, username) {
806
772
  console.error(`[Setup] Auto-generated User-Agent: ${autoAgent}`);
807
773
  return autoAgent;
808
774
  }
809
- const fallbackAgent = `RedditMCPServer/${VERSION}`;
810
- 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.");
811
777
  return fallbackAgent;
812
778
  }
813
779
  function buildSafeModeConfig(safeMode) {
@@ -914,7 +880,9 @@ async function setupRedditClient() {
914
880
  process.exit(1);
915
881
  }
916
882
  }
917
- 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({
918
886
  name: "reddit-mcp-server",
919
887
  version: VERSION,
920
888
  instructions: `A comprehensive Reddit MCP server that provides tools for interacting with Reddit API.
@@ -931,23 +899,16 @@ Available capabilities:
931
899
  For write operations (posting, replying, editing, deleting), ensure REDDIT_USERNAME and REDDIT_PASSWORD are configured.`,
932
900
  ...process.env.OAUTH_ENABLED === "true" && { authenticate: async (request) => {
933
901
  const authHeader = request.headers.authorization;
934
- const expectedToken = process.env.OAUTH_TOKEN;
935
- if (!expectedToken) {
936
- const token = Array.from({ length: 32 }, () => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".charAt(Math.floor(Math.random() * 62))).join("");
937
- console.log(`[Auth] Generated OAuth token: ${token}`);
938
- throw new Response(JSON.stringify({
939
- error: "No OAuth token configured",
940
- generatedToken: token
941
- }), {
942
- status: 401,
943
- headers: { "Content-Type": "application/json" }
944
- });
945
- }
946
902
  if (!(authHeader === null || authHeader === void 0 ? void 0 : authHeader.startsWith("Bearer "))) throw new Response(null, {
947
903
  status: 401,
948
904
  statusText: "Missing or invalid Authorization header"
949
905
  });
950
- 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, {
951
912
  status: 403,
952
913
  statusText: "Invalid token"
953
914
  });
@@ -957,7 +918,7 @@ For write operations (posting, replying, editing, deleting), ensure REDDIT_USERN
957
918
  server.addTool({
958
919
  name: "test_reddit_mcp_server",
959
920
  description: "Test the Reddit MCP Server connection and configuration",
960
- parameters: zod.z.object({}),
921
+ parameters: z.object({}),
961
922
  execute: async () => {
962
923
  const client = getRedditClient();
963
924
  const hasAuth = client ? "✓" : "✗";
@@ -974,7 +935,7 @@ Ready to handle Reddit API requests!`;
974
935
  server.addTool({
975
936
  name: "get_user_info",
976
937
  description: "Get detailed information about a Reddit user including karma, account status, and activity analysis",
977
- 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)") }),
978
939
  execute: async (args) => {
979
940
  const client = getRedditClient();
980
941
  if (!client) throw new Error("Reddit client not initialized");
@@ -992,7 +953,7 @@ server.addTool({
992
953
  - Profile URL: ${formattedUser.profileUrl}
993
954
 
994
955
  ## Activity Analysis
995
- - ${formattedUser.activityAnalysis.replace(/\n - /g, "\n- ")}
956
+ - ${formattedUser.activityAnalysis.replace(/\n {2}- /g, "\n- ")}
996
957
 
997
958
  ## Recommendations
998
959
  - ${formattedUser.recommendations.replace(/\n {2}- /g, "\n- ")}`;
@@ -1001,14 +962,14 @@ server.addTool({
1001
962
  server.addTool({
1002
963
  name: "get_user_posts",
1003
964
  description: "Get recent posts by a Reddit user with sorting and filtering options",
1004
- parameters: zod.z.object({
1005
- username: zod.z.string().describe("The Reddit username (without u/ prefix)"),
1006
- sort: zod.z.enum([
965
+ parameters: z.object({
966
+ username: z.string().describe("The Reddit username (without u/ prefix)"),
967
+ sort: z.enum([
1007
968
  "new",
1008
969
  "hot",
1009
970
  "top"
1010
971
  ]).default("new").describe("Sort order for posts"),
1011
- time_filter: zod.z.enum([
972
+ time_filter: z.enum([
1012
973
  "hour",
1013
974
  "day",
1014
975
  "week",
@@ -1016,7 +977,7 @@ server.addTool({
1016
977
  "year",
1017
978
  "all"
1018
979
  ]).default("all").describe("Time filter for top posts"),
1019
- 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")
1020
981
  }),
1021
982
  execute: async (args) => {
1022
983
  const client = getRedditClient();
@@ -1044,14 +1005,14 @@ ${postSummaries}`;
1044
1005
  server.addTool({
1045
1006
  name: "get_user_comments",
1046
1007
  description: "Get recent comments by a Reddit user with sorting and filtering options",
1047
- parameters: zod.z.object({
1048
- username: zod.z.string().describe("The Reddit username (without u/ prefix)"),
1049
- sort: zod.z.enum([
1008
+ parameters: z.object({
1009
+ username: z.string().describe("The Reddit username (without u/ prefix)"),
1010
+ sort: z.enum([
1050
1011
  "new",
1051
1012
  "hot",
1052
1013
  "top"
1053
1014
  ]).default("new").describe("Sort order for comments"),
1054
- time_filter: zod.z.enum([
1015
+ time_filter: z.enum([
1055
1016
  "hour",
1056
1017
  "day",
1057
1018
  "week",
@@ -1059,7 +1020,7 @@ server.addTool({
1059
1020
  "year",
1060
1021
  "all"
1061
1022
  ]).default("all").describe("Time filter for top comments"),
1062
- 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")
1063
1024
  }),
1064
1025
  execute: async (args) => {
1065
1026
  const client = getRedditClient();
@@ -1071,7 +1032,7 @@ server.addTool({
1071
1032
  });
1072
1033
  if (comments.length === 0) return `No comments found for u/${args.username} with the specified filters.`;
1073
1034
  const commentSummaries = comments.map((comment, index) => {
1074
- 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;
1075
1036
  const flags = [...comment.edited ? ["*(edited)*"] : [], ...comment.isSubmitter ? ["**OP**"] : []];
1076
1037
  return `### ${index + 1}. Comment ${flags.join(" ")}
1077
1038
  In r/${comment.subreddit} on "${comment.submissionTitle}"
@@ -1090,9 +1051,9 @@ ${commentSummaries}`;
1090
1051
  server.addTool({
1091
1052
  name: "get_reddit_post",
1092
1053
  description: "Get detailed information about a specific Reddit post including content, stats, and engagement analysis",
1093
- parameters: zod.z.object({
1094
- subreddit: zod.z.string().describe("The subreddit name (without r/ prefix)"),
1095
- 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")
1096
1057
  }),
1097
1058
  execute: async (args) => {
1098
1059
  const client = getRedditClient();
@@ -1123,7 +1084,7 @@ ${formattedPost.content}
1123
1084
  - Short Link: ${formattedPost.links.shortLink}
1124
1085
 
1125
1086
  ## Engagement Analysis
1126
- - ${formattedPost.engagementAnalysis.replace(/\n - /g, "\n- ")}
1087
+ - ${formattedPost.engagementAnalysis.replace(/\n {2}- /g, "\n- ")}
1127
1088
 
1128
1089
  ## Best Time to Engage
1129
1090
  ${formattedPost.bestTimeToEngage}`;
@@ -1132,9 +1093,9 @@ ${formattedPost.bestTimeToEngage}`;
1132
1093
  server.addTool({
1133
1094
  name: "get_top_posts",
1134
1095
  description: "Get top posts from a subreddit or from the Reddit home feed",
1135
- parameters: zod.z.object({
1136
- subreddit: zod.z.string().optional().describe("The subreddit name (without r/ prefix). Leave empty for home feed"),
1137
- 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([
1138
1099
  "hour",
1139
1100
  "day",
1140
1101
  "week",
@@ -1142,7 +1103,7 @@ server.addTool({
1142
1103
  "year",
1143
1104
  "all"
1144
1105
  ]).default("week").describe("Time period for top posts"),
1145
- 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")
1146
1107
  }),
1147
1108
  execute: async (args) => {
1148
1109
  const client = getRedditClient();
@@ -1163,7 +1124,7 @@ ${postSummaries}`;
1163
1124
  server.addTool({
1164
1125
  name: "get_subreddit_info",
1165
1126
  description: "Get detailed information about a subreddit including description, stats, and community analysis",
1166
- 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)") }),
1167
1128
  execute: async (args) => {
1168
1129
  const client = getRedditClient();
1169
1130
  if (!client) throw new Error("Reddit client not initialized");
@@ -1191,16 +1152,16 @@ ${formattedSubreddit.description.full}
1191
1152
  - Wiki: ${formattedSubreddit.links.wiki}
1192
1153
 
1193
1154
  ## Community Analysis
1194
- - ${formattedSubreddit.communityAnalysis.replace(/\n - /g, "\n- ")}
1155
+ - ${formattedSubreddit.communityAnalysis.replace(/\n {2}- /g, "\n- ")}
1195
1156
 
1196
1157
  ## Engagement Tips
1197
- - ${formattedSubreddit.engagementTips.replace(/\n - /g, "\n- ")}`;
1158
+ - ${formattedSubreddit.engagementTips.replace(/\n {2}- /g, "\n- ")}`;
1198
1159
  }
1199
1160
  });
1200
1161
  server.addTool({
1201
1162
  name: "get_trending_subreddits",
1202
1163
  description: "Get a list of currently trending subreddits",
1203
- parameters: zod.z.object({}),
1164
+ parameters: z.object({}),
1204
1165
  execute: async () => {
1205
1166
  const client = getRedditClient();
1206
1167
  if (!client) throw new Error("Reddit client not initialized");
@@ -1212,17 +1173,17 @@ ${(await client.getTrendingSubreddits()).map((subreddit, index) => `${index + 1}
1212
1173
  server.addTool({
1213
1174
  name: "search_reddit",
1214
1175
  description: "Search Reddit for posts and content across subreddits",
1215
- parameters: zod.z.object({
1216
- query: zod.z.string().describe("Search query"),
1217
- subreddit: zod.z.string().optional().describe("Limit search to specific subreddit (without r/ prefix)"),
1218
- 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([
1219
1180
  "relevance",
1220
1181
  "hot",
1221
1182
  "top",
1222
1183
  "new",
1223
1184
  "comments"
1224
1185
  ]).default("relevance").describe("Sort order"),
1225
- time_filter: zod.z.enum([
1186
+ time_filter: z.enum([
1226
1187
  "hour",
1227
1188
  "day",
1228
1189
  "week",
@@ -1230,8 +1191,8 @@ server.addTool({
1230
1191
  "year",
1231
1192
  "all"
1232
1193
  ]).default("all").describe("Time filter"),
1233
- limit: zod.z.number().min(1).max(100).default(10).describe("Number of results"),
1234
- type: zod.z.enum([
1194
+ limit: z.number().min(1).max(100).default(10).describe("Number of results"),
1195
+ type: z.enum([
1235
1196
  "link",
1236
1197
  "sr",
1237
1198
  "user"
@@ -1273,11 +1234,11 @@ ${searchResults}`;
1273
1234
  server.addTool({
1274
1235
  name: "create_post",
1275
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.",
1276
- parameters: zod.z.object({
1277
- subreddit: zod.z.string().describe("The subreddit name (without r/ prefix)"),
1278
- title: zod.z.string().describe("The post title"),
1279
- content: zod.z.string().describe("The post content (text for self posts, URL for link posts)"),
1280
- 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")
1281
1242
  }),
1282
1243
  execute: async (args) => {
1283
1244
  const client = getRedditClient();
@@ -1298,9 +1259,9 @@ Your post has been successfully submitted to r/${formattedPost.subreddit}.`;
1298
1259
  server.addTool({
1299
1260
  name: "reply_to_post",
1300
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.",
1301
- parameters: zod.z.object({
1302
- post_id: zod.z.string().describe("The Reddit post ID (thing_id, e.g., t3_xxxxx for posts, t1_xxxxx for comments)"),
1303
- 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")
1304
1265
  }),
1305
1266
  execute: async (args) => {
1306
1267
  const client = getRedditClient();
@@ -1320,7 +1281,7 @@ Your reply has been successfully posted.`;
1320
1281
  server.addTool({
1321
1282
  name: "delete_post",
1322
1283
  description: "Delete your own Reddit post (requires REDDIT_USERNAME and REDDIT_PASSWORD). WARNING: This action is permanent and cannot be undone!",
1323
- 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.") }),
1324
1285
  execute: async (args) => {
1325
1286
  const client = getRedditClient();
1326
1287
  if (!client) throw new Error("Reddit client not initialized");
@@ -1336,7 +1297,7 @@ The post ${args.thing_id} has been permanently deleted from Reddit.
1336
1297
  server.addTool({
1337
1298
  name: "delete_comment",
1338
1299
  description: "Delete your own Reddit comment (requires REDDIT_USERNAME and REDDIT_PASSWORD). WARNING: This action is permanent and cannot be undone!",
1339
- 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.") }),
1340
1301
  execute: async (args) => {
1341
1302
  const client = getRedditClient();
1342
1303
  if (!client) throw new Error("Reddit client not initialized");
@@ -1352,9 +1313,9 @@ The comment ${args.thing_id} has been permanently deleted from Reddit.
1352
1313
  server.addTool({
1353
1314
  name: "edit_post",
1354
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.",
1355
- parameters: zod.z.object({
1356
- 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."),
1357
- 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.")
1358
1319
  }),
1359
1320
  execute: async (args) => {
1360
1321
  const client = getRedditClient();
@@ -1375,9 +1336,9 @@ The post ${args.thing_id} has been updated with your new content.
1375
1336
  server.addTool({
1376
1337
  name: "edit_comment",
1377
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.",
1378
- parameters: zod.z.object({
1379
- 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."),
1380
- 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.")
1381
1342
  }),
1382
1343
  execute: async (args) => {
1383
1344
  const client = getRedditClient();
@@ -1394,10 +1355,10 @@ The comment ${args.thing_id} has been updated with your new content.
1394
1355
  server.addTool({
1395
1356
  name: "get_post_comments",
1396
1357
  description: "Get comments from a specific Reddit post",
1397
- parameters: zod.z.object({
1398
- post_id: zod.z.string().describe("The Reddit post ID"),
1399
- subreddit: zod.z.string().describe("The subreddit name (without r/ prefix)"),
1400
- 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([
1401
1362
  "best",
1402
1363
  "top",
1403
1364
  "new",
@@ -1405,7 +1366,7 @@ server.addTool({
1405
1366
  "old",
1406
1367
  "qa"
1407
1368
  ]).default("best").describe("Comment sort order"),
1408
- 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")
1409
1370
  }),
1410
1371
  execute: async (args) => {
1411
1372
  const client = getRedditClient();
@@ -1415,8 +1376,8 @@ server.addTool({
1415
1376
  sort: args.sort,
1416
1377
  limit: args.limit
1417
1378
  });
1418
- const post = data.post;
1419
- const comments = data.comments;
1379
+ const { post } = data;
1380
+ const { comments } = data;
1420
1381
  let response = `# Comments for: ${post.title}
1421
1382
 
1422
1383
  **Post by u/${post.author} in r/${post.subreddit}**
@@ -1449,7 +1410,7 @@ async function main() {
1449
1410
  await setupRedditClient();
1450
1411
  const useHttp = process.env.TRANSPORT_TYPE === "httpStream" || process.env.TRANSPORT_TYPE === "http";
1451
1412
  const port = parseInt(process.env.PORT || "3000");
1452
- const host = process.env.HOST || "0.0.0.0";
1413
+ const host = process.env.HOST || "127.0.0.1";
1453
1414
  if (useHttp) {
1454
1415
  console.error(`[Setup] Starting HTTP server on ${host}:${port}`);
1455
1416
  await server.start({
@@ -1482,4 +1443,5 @@ process.on("SIGTERM", async () => {
1482
1443
  main().catch(console.error);
1483
1444
 
1484
1445
  //#endregion
1446
+ export { };
1485
1447
  //# sourceMappingURL=index.js.map