@daanrongen/reddit-mcp 1.0.1 → 1.1.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.
Files changed (2) hide show
  1. package/dist/main.js +212 -265
  2. package/package.json +3 -3
package/dist/main.js CHANGED
@@ -27263,13 +27263,15 @@ class RedditNotFoundError extends exports_Data.TaggedError("RedditNotFoundError"
27263
27263
  class RedditAuth extends exports_Context.Tag("RedditAuth")() {
27264
27264
  }
27265
27265
 
27266
- // src/infra/RedditAuthLive.ts
27267
- var REDDIT_TOKEN_URL = "https://www.reddit.com/api/v1/access_token";
27268
- var REFRESH_BUFFER_MS = 5 * 60 * 1000;
27266
+ // src/infra/utils.ts
27269
27267
  var buildUserAgent = (username) => {
27270
27268
  const user = exports_Option.getOrElse(username, () => "your-username");
27271
27269
  return `reddit-mcp/1.0 by ${user}`;
27272
27270
  };
27271
+
27272
+ // src/infra/RedditAuthLive.ts
27273
+ var REDDIT_TOKEN_URL = "https://www.reddit.com/api/v1/access_token";
27274
+ var REFRESH_BUFFER_MS = 5 * 60 * 1000;
27273
27275
  var fetchToken = (clientId, clientSecret, grantParams, userAgent) => exports_Effect.tryPromise({
27274
27276
  try: async () => {
27275
27277
  const credentials = btoa(`${clientId}:${clientSecret}`);
@@ -27338,14 +27340,10 @@ class RedditClient extends exports_Context.Tag("RedditClient")() {
27338
27340
 
27339
27341
  // src/infra/RedditClientLive.ts
27340
27342
  var REDDIT_API_BASE = "https://oauth.reddit.com";
27341
- var buildUserAgent2 = (username) => {
27342
- const user = exports_Option.getOrElse(username, () => "your-username");
27343
- return `reddit-mcp/1.0 by ${user}`;
27344
- };
27345
27343
  var RedditClientLive = exports_Layer.effect(RedditClient, exports_Effect.gen(function* () {
27346
27344
  const auth = yield* RedditAuth;
27347
27345
  const usernameOption = yield* exports_Effect.orDie(RedditUsernameConfig);
27348
- const userAgent = buildUserAgent2(usernameOption);
27346
+ const userAgent = buildUserAgent(usernameOption);
27349
27347
  const makeRequest = (method, path, params, body) => exports_Effect.gen(function* () {
27350
27348
  const token = yield* auth.getAccessToken();
27351
27349
  const url3 = new URL(`${REDDIT_API_BASE}${path}`);
@@ -34991,6 +34989,11 @@ var EMPTY_COMPLETION_RESULT = {
34991
34989
  }
34992
34990
  };
34993
34991
 
34992
+ // src/mcp/types.ts
34993
+ var buildPaginationString = (after3) => after3 ? `
34994
+
34995
+ More results available. Use after="${after3}" to fetch the next page.` : "";
34996
+
34994
34997
  // src/mcp/utils.ts
34995
34998
  var formatSuccess = (data) => ({
34996
34999
  content: [
@@ -35010,6 +35013,12 @@ var formatError2 = (cause3) => ({
35010
35013
  isError: true
35011
35014
  });
35012
35015
  var WRITE_AUTH_ERROR = "Write operations require REDDIT_REFRESH_TOKEN. " + "See README for setup instructions on obtaining a refresh token via the OAuth2 authorization code flow.";
35016
+ var runTool = async (runtime4, effect2) => {
35017
+ const exit3 = await runtime4.runPromiseExit(effect2);
35018
+ if (exit3._tag === "Failure")
35019
+ return formatError2(exit3.cause);
35020
+ return formatSuccess(exit3.value);
35021
+ };
35013
35022
 
35014
35023
  // src/mcp/tools/browse.ts
35015
35024
  var formatPost = (post) => {
@@ -35061,27 +35070,24 @@ var registerBrowseTools = (server, runtime4) => {
35061
35070
  destructiveHint: false,
35062
35071
  idempotentHint: false,
35063
35072
  openWorldHint: true
35064
- }, async ({ subreddit, feed, t, limit, after: after3 }) => {
35065
- const result = await runtime4.runPromiseExit(exports_Effect.gen(function* () {
35066
- const client = yield* RedditClient;
35067
- const feedPath = feed ?? "hot";
35068
- const data = yield* client.get(`/r/${subreddit}/${feedPath}`, { t: t ?? "day", limit: limit ?? 25, after: after3 });
35069
- const posts = (data.data?.children ?? []).map((c) => c.data ?? {});
35070
- if (!posts.length)
35071
- return `No posts found in r/${subreddit}.`;
35072
- const pagination = data.data?.after ? `
35073
-
35074
- More results available. Use after="${data.data.after}" to fetch the next page.` : "";
35075
- return `r/${subreddit} \u2014 ${feedPath} posts:
35073
+ }, async ({ subreddit, feed, t, limit, after: after3 }) => runTool(runtime4, exports_Effect.gen(function* () {
35074
+ const client = yield* RedditClient;
35075
+ const feedPath = feed ?? "hot";
35076
+ const data = yield* client.get(`/r/${subreddit}/${feedPath}`, {
35077
+ t: t ?? "day",
35078
+ limit: limit ?? 25,
35079
+ after: after3
35080
+ });
35081
+ const posts = (data.data?.children ?? []).map((c) => c.data ?? {});
35082
+ if (!posts.length)
35083
+ return `No posts found in r/${subreddit}.`;
35084
+ const pagination = buildPaginationString(data.data?.after);
35085
+ return `r/${subreddit} \u2014 ${feedPath} posts:
35076
35086
 
35077
35087
  ${posts.map(formatPost).join(`
35078
35088
 
35079
35089
  `)}` + pagination;
35080
- }));
35081
- if (result._tag === "Failure")
35082
- return formatError2(result.cause);
35083
- return formatSuccess(result.value);
35084
- });
35090
+ })));
35085
35091
  server.tool("get_post", "Fetch a single Reddit post by ID or permalink URL, including the top-level comments.", {
35086
35092
  id: exports_external.string().optional().describe("Reddit post ID (e.g. 'abc123') \u2014 provide either this or 'url', not both"),
35087
35093
  url: exports_external.string().optional().describe("Full Reddit permalink URL (e.g. 'https://reddit.com/r/programming/comments/abc123/...') \u2014 provide either this or 'id'"),
@@ -35094,9 +35100,13 @@ ${posts.map(formatPost).join(`
35094
35100
  openWorldHint: true
35095
35101
  }, async ({ id, url: url3, comment_limit }) => {
35096
35102
  if (!id && !url3) {
35097
- return formatSuccess("Provide either 'id' or 'url' to fetch a post.");
35103
+ return {
35104
+ content: [
35105
+ { type: "text", text: "Provide either 'id' or 'url' to fetch a post." }
35106
+ ]
35107
+ };
35098
35108
  }
35099
- const result = await runtime4.runPromiseExit(exports_Effect.gen(function* () {
35109
+ return runTool(runtime4, exports_Effect.gen(function* () {
35100
35110
  const client = yield* RedditClient;
35101
35111
  let path;
35102
35112
  if (url3) {
@@ -35109,7 +35119,6 @@ ${posts.map(formatPost).join(`
35109
35119
  return `Post with ID "${id}" not found.`;
35110
35120
  }
35111
35121
  path = post2.permalink?.replace(/\/$/, "") ?? `/r/${post2.subreddit}/comments/${id}`;
35112
- return formatPostWithComments(post2, [], comment_limit ?? 10);
35113
35122
  }
35114
35123
  const listingData = yield* client.get(path, { limit: comment_limit ?? 10 });
35115
35124
  const post = listingData[0]?.data?.children?.[0]?.data ?? {};
@@ -35117,9 +35126,6 @@ ${posts.map(formatPost).join(`
35117
35126
  const comments = commentChildren.filter((c) => c.kind === "t1").map((c) => c.data ?? {});
35118
35127
  return formatPostWithComments(post, comments, comment_limit ?? 10);
35119
35128
  }));
35120
- if (result._tag === "Failure")
35121
- return formatError2(result.cause);
35122
- return formatSuccess(result.value);
35123
35129
  });
35124
35130
  server.tool("get_comments", "Fetch comments for a Reddit post. Supports sorting and depth control.", {
35125
35131
  subreddit: exports_external.string().min(1).describe("Subreddit name without r/ prefix"),
@@ -35133,31 +35139,26 @@ ${posts.map(formatPost).join(`
35133
35139
  destructiveHint: false,
35134
35140
  idempotentHint: true,
35135
35141
  openWorldHint: true
35136
- }, async ({ subreddit, post_id, sort: sort2, limit, depth }) => {
35137
- const result = await runtime4.runPromiseExit(exports_Effect.gen(function* () {
35138
- const client = yield* RedditClient;
35139
- const data = yield* client.get(`/r/${subreddit}/comments/${post_id}`, {
35140
- sort: sort2 ?? "confidence",
35141
- limit: limit ?? 25,
35142
- depth: depth ?? 3
35143
- });
35144
- const commentChildren = data[1]?.data?.children ?? [];
35145
- const comments = commentChildren.filter((c) => c.kind === "t1").map((c) => c.data ?? {});
35146
- if (!comments.length)
35147
- return "No comments found for this post.";
35148
- const formatted = comments.map((c) => formatComment(c)).filter(Boolean).join(`
35142
+ }, async ({ subreddit, post_id, sort: sort2, limit, depth }) => runTool(runtime4, exports_Effect.gen(function* () {
35143
+ const client = yield* RedditClient;
35144
+ const data = yield* client.get(`/r/${subreddit}/comments/${post_id}`, {
35145
+ sort: sort2 ?? "confidence",
35146
+ limit: limit ?? 25,
35147
+ depth: depth ?? 3
35148
+ });
35149
+ const commentChildren = data[1]?.data?.children ?? [];
35150
+ const comments = commentChildren.filter((c) => c.kind === "t1").map((c) => c.data ?? {});
35151
+ if (!comments.length)
35152
+ return "No comments found for this post.";
35153
+ const formatted = comments.map((c) => formatComment(c)).filter(Boolean).join(`
35149
35154
 
35150
35155
  ---
35151
35156
 
35152
35157
  `);
35153
- return `Comments for r/${subreddit}/comments/${post_id} (sort: ${sort2 ?? "confidence"}):
35158
+ return `Comments for r/${subreddit}/comments/${post_id} (sort: ${sort2 ?? "confidence"}):
35154
35159
 
35155
35160
  ${formatted}`;
35156
- }));
35157
- if (result._tag === "Failure")
35158
- return formatError2(result.cause);
35159
- return formatSuccess(result.value);
35160
- });
35161
+ })));
35161
35162
  server.tool("get_subreddit_info", "Fetch metadata for a subreddit \u2014 subscriber count, description, rules, and settings.", {
35162
35163
  subreddit: exports_external.string().min(1).describe("Subreddit name without r/ prefix")
35163
35164
  }, {
@@ -35166,41 +35167,36 @@ ${formatted}`;
35166
35167
  destructiveHint: false,
35167
35168
  idempotentHint: true,
35168
35169
  openWorldHint: true
35169
- }, async ({ subreddit }) => {
35170
- const result = await runtime4.runPromiseExit(exports_Effect.gen(function* () {
35171
- const client = yield* RedditClient;
35172
- const [aboutData, rulesData] = yield* exports_Effect.all([
35173
- client.get(`/r/${subreddit}/about`),
35174
- client.get(`/r/${subreddit}/about/rules`)
35175
- ], { concurrency: "unbounded" });
35176
- const about = aboutData.data ?? {};
35177
- const rules = rulesData.rules ?? [];
35178
- const lines = [
35179
- `r/${about.display_name ?? subreddit}`,
35180
- `Title: ${about.title ?? "?"}`,
35181
- `Subscribers: ${about.subscribers?.toLocaleString() ?? "?"}`,
35182
- `Active users: ${about.active_user_count?.toLocaleString() ?? "?"}`,
35183
- `NSFW: ${about.over18 ? "yes" : "no"}`,
35184
- ``,
35185
- `Description:`,
35186
- about.public_description ?? "(no description)"
35187
- ];
35188
- if (rules.length) {
35189
- lines.push("", "Rules:");
35190
- for (const rule of rules) {
35191
- lines.push(` \u2022 ${rule.short_name ?? "Rule"}: ${rule.description ?? ""}`);
35192
- }
35193
- }
35194
- if (about.submit_text) {
35195
- lines.push("", "Submission guidelines:", about.submit_text);
35196
- }
35197
- return lines.join(`
35170
+ }, async ({ subreddit }) => runTool(runtime4, exports_Effect.gen(function* () {
35171
+ const client = yield* RedditClient;
35172
+ const [aboutData, rulesData] = yield* exports_Effect.all([
35173
+ client.get(`/r/${subreddit}/about`),
35174
+ client.get(`/r/${subreddit}/about/rules`)
35175
+ ], { concurrency: "unbounded" });
35176
+ const about = aboutData.data ?? {};
35177
+ const rules = rulesData.rules ?? [];
35178
+ const lines = [
35179
+ `r/${about.display_name ?? subreddit}`,
35180
+ `Title: ${about.title ?? "?"}`,
35181
+ `Subscribers: ${about.subscribers?.toLocaleString() ?? "?"}`,
35182
+ `Active users: ${about.active_user_count?.toLocaleString() ?? "?"}`,
35183
+ `NSFW: ${about.over18 ? "yes" : "no"}`,
35184
+ ``,
35185
+ `Description:`,
35186
+ about.public_description ?? "(no description)"
35187
+ ];
35188
+ if (rules.length) {
35189
+ lines.push("", "Rules:");
35190
+ for (const rule of rules) {
35191
+ lines.push(` \u2022 ${rule.short_name ?? "Rule"}: ${rule.description ?? ""}`);
35192
+ }
35193
+ }
35194
+ if (about.submit_text) {
35195
+ lines.push("", "Submission guidelines:", about.submit_text);
35196
+ }
35197
+ return lines.join(`
35198
35198
  `);
35199
- }));
35200
- if (result._tag === "Failure")
35201
- return formatError2(result.cause);
35202
- return formatSuccess(result.value);
35203
- });
35199
+ })));
35204
35200
  };
35205
35201
  var formatPostWithComments = (post, comments, commentLimit) => {
35206
35202
  const postLines = [
@@ -35259,31 +35255,26 @@ var registerSearchTools = (server, runtime4) => {
35259
35255
  destructiveHint: false,
35260
35256
  idempotentHint: true,
35261
35257
  openWorldHint: true
35262
- }, async ({ q, subreddit, sort: sort2, t, limit }) => {
35263
- const result = await runtime4.runPromiseExit(exports_Effect.gen(function* () {
35264
- const client = yield* RedditClient;
35265
- const path = subreddit ? `/r/${subreddit}/search` : "/search";
35266
- const data = yield* client.get(path, {
35267
- q,
35268
- sort: sort2 ?? "relevance",
35269
- t: t ?? "all",
35270
- limit: limit ?? 25,
35271
- restrict_sr: subreddit ? "true" : undefined,
35272
- type: "link"
35273
- });
35274
- const posts = extractPosts(data);
35275
- if (!posts.length)
35276
- return `No posts found for query: "${q}"`;
35277
- return `Search results for "${q}"${subreddit ? ` in r/${subreddit}` : ""}:
35258
+ }, async ({ q, subreddit, sort: sort2, t, limit }) => runTool(runtime4, exports_Effect.gen(function* () {
35259
+ const client = yield* RedditClient;
35260
+ const path = subreddit ? `/r/${subreddit}/search` : "/search";
35261
+ const data = yield* client.get(path, {
35262
+ q,
35263
+ sort: sort2 ?? "relevance",
35264
+ t: t ?? "all",
35265
+ limit: limit ?? 25,
35266
+ restrict_sr: subreddit ? "true" : undefined,
35267
+ type: "link"
35268
+ });
35269
+ const posts = extractPosts(data);
35270
+ if (!posts.length)
35271
+ return `No posts found for query: "${q}"`;
35272
+ return `Search results for "${q}"${subreddit ? ` in r/${subreddit}` : ""}:
35278
35273
 
35279
35274
  ${posts.map(formatPost2).join(`
35280
35275
 
35281
35276
  `)}`;
35282
- }));
35283
- if (result._tag === "Failure")
35284
- return formatError2(result.cause);
35285
- return formatSuccess(result.value);
35286
- });
35277
+ })));
35287
35278
  server.tool("search_subreddits", "Search for subreddits by topic or keyword. Returns matching subreddits with subscriber counts and descriptions.", {
35288
35279
  q: exports_external.string().min(1).describe("Topic or keyword to search subreddits for"),
35289
35280
  limit: exports_external.number().int().min(1).max(100).optional().describe("Number of results to return (1-100, default: 25)")
@@ -35293,30 +35284,25 @@ ${posts.map(formatPost2).join(`
35293
35284
  destructiveHint: false,
35294
35285
  idempotentHint: true,
35295
35286
  openWorldHint: true
35296
- }, async ({ q, limit }) => {
35297
- const result = await runtime4.runPromiseExit(exports_Effect.gen(function* () {
35298
- const client = yield* RedditClient;
35299
- const data = yield* client.get("/subreddits/search", { q, limit: limit ?? 25 });
35300
- const subs = (data.data?.children ?? []).map((c) => c.data ?? {});
35301
- if (!subs.length)
35302
- return `No subreddits found for: "${q}"`;
35303
- const lines = subs.map((s) => {
35304
- const name = s.display_name ?? "?";
35305
- const subscribers = s.subscribers?.toLocaleString() ?? "?";
35306
- const desc = s.public_description ? `
35287
+ }, async ({ q, limit }) => runTool(runtime4, exports_Effect.gen(function* () {
35288
+ const client = yield* RedditClient;
35289
+ const data = yield* client.get("/subreddits/search", { q, limit: limit ?? 25 });
35290
+ const subs = (data.data?.children ?? []).map((c) => c.data ?? {});
35291
+ if (!subs.length)
35292
+ return `No subreddits found for: "${q}"`;
35293
+ const lines = subs.map((s) => {
35294
+ const name = s.display_name ?? "?";
35295
+ const subscribers = s.subscribers?.toLocaleString() ?? "?";
35296
+ const desc = s.public_description ? `
35307
35297
  ${s.public_description.slice(0, 150)}${(s.public_description?.length ?? 0) > 150 ? "\u2026" : ""}` : "";
35308
- return `r/${name} (${subscribers} subscribers)${desc}`;
35309
- });
35310
- return `Subreddits matching "${q}":
35298
+ return `r/${name} (${subscribers} subscribers)${desc}`;
35299
+ });
35300
+ return `Subreddits matching "${q}":
35311
35301
 
35312
35302
  ${lines.join(`
35313
35303
 
35314
35304
  `)}`;
35315
- }));
35316
- if (result._tag === "Failure")
35317
- return formatError2(result.cause);
35318
- return formatSuccess(result.value);
35319
- });
35305
+ })));
35320
35306
  };
35321
35307
 
35322
35308
  // src/mcp/tools/user.ts
@@ -35356,19 +35342,14 @@ var registerUserTools = (server, runtime4) => {
35356
35342
  destructiveHint: false,
35357
35343
  idempotentHint: true,
35358
35344
  openWorldHint: true
35359
- }, async ({ username }) => {
35360
- const result = await runtime4.runPromiseExit(exports_Effect.gen(function* () {
35361
- const client = yield* RedditClient;
35362
- const data = yield* client.get(`/user/${username}/about`);
35363
- const user = data.data;
35364
- if (!user)
35365
- return `User u/${username} not found.`;
35366
- return formatUser(user);
35367
- }));
35368
- if (result._tag === "Failure")
35369
- return formatError2(result.cause);
35370
- return formatSuccess(result.value);
35371
- });
35345
+ }, async ({ username }) => runTool(runtime4, exports_Effect.gen(function* () {
35346
+ const client = yield* RedditClient;
35347
+ const data = yield* client.get(`/user/${username}/about`);
35348
+ const user = data.data;
35349
+ if (!user)
35350
+ return `User u/${username} not found.`;
35351
+ return formatUser(user);
35352
+ })));
35372
35353
  server.tool("get_user_posts", "Fetch a Reddit user's submitted posts.", {
35373
35354
  username: exports_external.string().min(1).describe("Reddit username without u/ prefix"),
35374
35355
  sort: exports_external.enum(["hot", "new", "top", "controversial"]).optional().describe("Sort order (default: new)"),
@@ -35381,31 +35362,24 @@ var registerUserTools = (server, runtime4) => {
35381
35362
  destructiveHint: false,
35382
35363
  idempotentHint: false,
35383
35364
  openWorldHint: true
35384
- }, async ({ username, sort: sort2, t, limit, after: after3 }) => {
35385
- const result = await runtime4.runPromiseExit(exports_Effect.gen(function* () {
35386
- const client = yield* RedditClient;
35387
- const data = yield* client.get(`/user/${username}/submitted`, {
35388
- sort: sort2 ?? "new",
35389
- t: t ?? "all",
35390
- limit: limit ?? 25,
35391
- after: after3
35392
- });
35393
- const posts = (data.data?.children ?? []).map((c) => c.data ?? {});
35394
- if (!posts.length)
35395
- return `u/${username} has no posts.`;
35396
- const pagination = data.data?.after ? `
35397
-
35398
- More results available. Use after="${data.data.after}" to fetch the next page.` : "";
35399
- return `Posts by u/${username}:
35365
+ }, async ({ username, sort: sort2, t, limit, after: after3 }) => runTool(runtime4, exports_Effect.gen(function* () {
35366
+ const client = yield* RedditClient;
35367
+ const data = yield* client.get(`/user/${username}/submitted`, {
35368
+ sort: sort2 ?? "new",
35369
+ t: t ?? "all",
35370
+ limit: limit ?? 25,
35371
+ after: after3
35372
+ });
35373
+ const posts = (data.data?.children ?? []).map((c) => c.data ?? {});
35374
+ if (!posts.length)
35375
+ return `u/${username} has no posts.`;
35376
+ const pagination = buildPaginationString(data.data?.after);
35377
+ return `Posts by u/${username}:
35400
35378
 
35401
35379
  ${posts.map(formatUserPost).join(`
35402
35380
 
35403
35381
  `)}${pagination}`;
35404
- }));
35405
- if (result._tag === "Failure")
35406
- return formatError2(result.cause);
35407
- return formatSuccess(result.value);
35408
- });
35382
+ })));
35409
35383
  server.tool("get_user_comments", "Fetch a Reddit user's comment history.", {
35410
35384
  username: exports_external.string().min(1).describe("Reddit username without u/ prefix"),
35411
35385
  sort: exports_external.enum(["hot", "new", "top", "controversial"]).optional().describe("Sort order (default: new)"),
@@ -35418,31 +35392,24 @@ ${posts.map(formatUserPost).join(`
35418
35392
  destructiveHint: false,
35419
35393
  idempotentHint: false,
35420
35394
  openWorldHint: true
35421
- }, async ({ username, sort: sort2, t, limit, after: after3 }) => {
35422
- const result = await runtime4.runPromiseExit(exports_Effect.gen(function* () {
35423
- const client = yield* RedditClient;
35424
- const data = yield* client.get(`/user/${username}/comments`, {
35425
- sort: sort2 ?? "new",
35426
- t: t ?? "all",
35427
- limit: limit ?? 25,
35428
- after: after3
35429
- });
35430
- const comments = (data.data?.children ?? []).map((c) => c.data ?? {});
35431
- if (!comments.length)
35432
- return `u/${username} has no comments.`;
35433
- const pagination = data.data?.after ? `
35434
-
35435
- More results available. Use after="${data.data.after}" to fetch the next page.` : "";
35436
- return `Comments by u/${username}:
35395
+ }, async ({ username, sort: sort2, t, limit, after: after3 }) => runTool(runtime4, exports_Effect.gen(function* () {
35396
+ const client = yield* RedditClient;
35397
+ const data = yield* client.get(`/user/${username}/comments`, {
35398
+ sort: sort2 ?? "new",
35399
+ t: t ?? "all",
35400
+ limit: limit ?? 25,
35401
+ after: after3
35402
+ });
35403
+ const comments = (data.data?.children ?? []).map((c) => c.data ?? {});
35404
+ if (!comments.length)
35405
+ return `u/${username} has no comments.`;
35406
+ const pagination = buildPaginationString(data.data?.after);
35407
+ return `Comments by u/${username}:
35437
35408
 
35438
35409
  ${comments.map(formatUserComment).join(`
35439
35410
 
35440
35411
  `)}` + pagination;
35441
- }));
35442
- if (result._tag === "Failure")
35443
- return formatError2(result.cause);
35444
- return formatSuccess(result.value);
35445
- });
35412
+ })));
35446
35413
  };
35447
35414
 
35448
35415
  // src/mcp/tools/write.ts
@@ -35467,43 +35434,38 @@ var registerWriteTools = (server, runtime4) => {
35467
35434
  destructiveHint: false,
35468
35435
  idempotentHint: false,
35469
35436
  openWorldHint: true
35470
- }, async ({ subreddit, title, kind, text, url: url3, nsfw, spoiler, flair_id, flair_text }) => {
35471
- const result = await runtime4.runPromiseExit(exports_Effect.gen(function* () {
35472
- const hasToken = yield* requireRefreshToken;
35473
- if (!hasToken)
35474
- return WRITE_AUTH_ERROR;
35475
- const client = yield* RedditClient;
35476
- const body = {
35477
- sr: subreddit,
35478
- title,
35479
- kind,
35480
- resubmit: true,
35481
- nsfw: nsfw ?? false,
35482
- spoiler: spoiler ?? false
35483
- };
35484
- if (kind === "self" && text)
35485
- body.text = text;
35486
- if (kind === "link" && url3)
35487
- body.url = url3;
35488
- if (flair_id)
35489
- body.flair_id = flair_id;
35490
- if (flair_text)
35491
- body.flair_text = flair_text;
35492
- const data = yield* client.post("/api/submit", body);
35493
- const errors4 = data.json?.errors ?? [];
35494
- if (errors4.length > 0) {
35495
- return `Post submission failed. Errors: ${JSON.stringify(errors4)}`;
35496
- }
35497
- const postUrl = data.json?.data?.url ?? "";
35498
- const postId = data.json?.data?.id ?? "";
35499
- return `Post submitted successfully!
35437
+ }, async ({ subreddit, title, kind, text, url: url3, nsfw, spoiler, flair_id, flair_text }) => runTool(runtime4, exports_Effect.gen(function* () {
35438
+ const hasToken = yield* requireRefreshToken;
35439
+ if (!hasToken)
35440
+ return WRITE_AUTH_ERROR;
35441
+ const client = yield* RedditClient;
35442
+ const body = {
35443
+ sr: subreddit,
35444
+ title,
35445
+ kind,
35446
+ resubmit: true,
35447
+ nsfw: nsfw ?? false,
35448
+ spoiler: spoiler ?? false
35449
+ };
35450
+ if (kind === "self" && text)
35451
+ body.text = text;
35452
+ if (kind === "link" && url3)
35453
+ body.url = url3;
35454
+ if (flair_id)
35455
+ body.flair_id = flair_id;
35456
+ if (flair_text)
35457
+ body.flair_text = flair_text;
35458
+ const data = yield* client.post("/api/submit", body);
35459
+ const errors4 = data.json?.errors ?? [];
35460
+ if (errors4.length > 0) {
35461
+ return `Post submission failed. Errors: ${JSON.stringify(errors4)}`;
35462
+ }
35463
+ const postUrl = data.json?.data?.url ?? "";
35464
+ const postId = data.json?.data?.id ?? "";
35465
+ return `Post submitted successfully!
35500
35466
  ID: ${postId}
35501
35467
  URL: ${postUrl}`;
35502
- }));
35503
- if (result._tag === "Failure")
35504
- return formatError2(result.cause);
35505
- return formatSuccess(result.value);
35506
- });
35468
+ })));
35507
35469
  server.tool("submit_comment", "Reply to a Reddit post or comment. Requires REDDIT_REFRESH_TOKEN to be configured.", {
35508
35470
  parent_id: exports_external.string().min(1).describe("Fullname of the parent \u2014 a post ID prefixed with t3_ (e.g. 't3_abc123') or a comment ID prefixed with t1_ (e.g. 't1_xyz789')"),
35509
35471
  text: exports_external.string().min(1).describe("Comment body in Markdown")
@@ -35513,31 +35475,26 @@ URL: ${postUrl}`;
35513
35475
  destructiveHint: false,
35514
35476
  idempotentHint: false,
35515
35477
  openWorldHint: true
35516
- }, async ({ parent_id, text }) => {
35517
- const result = await runtime4.runPromiseExit(exports_Effect.gen(function* () {
35518
- const hasToken = yield* requireRefreshToken;
35519
- if (!hasToken)
35520
- return WRITE_AUTH_ERROR;
35521
- const client = yield* RedditClient;
35522
- const data = yield* client.post("/api/comment", {
35523
- parent: parent_id,
35524
- text
35525
- });
35526
- const errors4 = data.json?.errors ?? [];
35527
- if (errors4.length > 0) {
35528
- return `Comment submission failed. Errors: ${JSON.stringify(errors4)}`;
35529
- }
35530
- const comment = data.json?.data?.things?.[0]?.data;
35531
- const commentId = comment?.id ?? "";
35532
- const permalink = comment?.permalink ?? "";
35533
- return `Comment submitted successfully!
35478
+ }, async ({ parent_id, text }) => runTool(runtime4, exports_Effect.gen(function* () {
35479
+ const hasToken = yield* requireRefreshToken;
35480
+ if (!hasToken)
35481
+ return WRITE_AUTH_ERROR;
35482
+ const client = yield* RedditClient;
35483
+ const data = yield* client.post("/api/comment", {
35484
+ parent: parent_id,
35485
+ text
35486
+ });
35487
+ const errors4 = data.json?.errors ?? [];
35488
+ if (errors4.length > 0) {
35489
+ return `Comment submission failed. Errors: ${JSON.stringify(errors4)}`;
35490
+ }
35491
+ const comment = data.json?.data?.things?.[0]?.data;
35492
+ const commentId = comment?.id ?? "";
35493
+ const permalink = comment?.permalink ?? "";
35494
+ return `Comment submitted successfully!
35534
35495
  ID: ${commentId}${permalink ? `
35535
35496
  URL: https://reddit.com${permalink}` : ""}`;
35536
- }));
35537
- if (result._tag === "Failure")
35538
- return formatError2(result.cause);
35539
- return formatSuccess(result.value);
35540
- });
35497
+ })));
35541
35498
  server.tool("vote", "Vote on a Reddit post or comment. Requires REDDIT_REFRESH_TOKEN to be configured.", {
35542
35499
  id: exports_external.string().min(1).describe("Fullname of the thing to vote on \u2014 post ID prefixed with t3_ (e.g. 't3_abc123') or comment ID prefixed with t1_ (e.g. 't1_xyz789')"),
35543
35500
  dir: exports_external.union([exports_external.literal(1), exports_external.literal(0), exports_external.literal(-1)]).describe("Vote direction: 1 = upvote, 0 = remove vote, -1 = downvote")
@@ -35547,20 +35504,15 @@ URL: https://reddit.com${permalink}` : ""}`;
35547
35504
  destructiveHint: false,
35548
35505
  idempotentHint: true,
35549
35506
  openWorldHint: true
35550
- }, async ({ id, dir }) => {
35551
- const result = await runtime4.runPromiseExit(exports_Effect.gen(function* () {
35552
- const hasToken = yield* requireRefreshToken;
35553
- if (!hasToken)
35554
- return WRITE_AUTH_ERROR;
35555
- const client = yield* RedditClient;
35556
- yield* client.post("/api/vote", { id, dir });
35557
- const label = dir === 1 ? "Upvoted" : dir === -1 ? "Downvoted" : "Vote removed from";
35558
- return `${label} ${id}`;
35559
- }));
35560
- if (result._tag === "Failure")
35561
- return formatError2(result.cause);
35562
- return formatSuccess(result.value);
35563
- });
35507
+ }, async ({ id, dir }) => runTool(runtime4, exports_Effect.gen(function* () {
35508
+ const hasToken = yield* requireRefreshToken;
35509
+ if (!hasToken)
35510
+ return WRITE_AUTH_ERROR;
35511
+ const client = yield* RedditClient;
35512
+ yield* client.post("/api/vote", { id, dir });
35513
+ const label = dir === 1 ? "Upvoted" : dir === -1 ? "Downvoted" : "Vote removed from";
35514
+ return `${label} ${id}`;
35515
+ })));
35564
35516
  server.tool("save_post", "Save or unsave a Reddit post or comment. Requires REDDIT_REFRESH_TOKEN to be configured.", {
35565
35517
  id: exports_external.string().min(1).describe("Fullname of the post or comment \u2014 e.g. 't3_abc123' for a post, 't1_xyz789' for a comment"),
35566
35518
  save: exports_external.boolean().describe("true to save, false to unsave")
@@ -35570,20 +35522,15 @@ URL: https://reddit.com${permalink}` : ""}`;
35570
35522
  destructiveHint: false,
35571
35523
  idempotentHint: true,
35572
35524
  openWorldHint: true
35573
- }, async ({ id, save }) => {
35574
- const result = await runtime4.runPromiseExit(exports_Effect.gen(function* () {
35575
- const hasToken = yield* requireRefreshToken;
35576
- if (!hasToken)
35577
- return WRITE_AUTH_ERROR;
35578
- const client = yield* RedditClient;
35579
- const path = save ? "/api/save" : "/api/unsave";
35580
- yield* client.post(path, { id });
35581
- return `${save ? "Saved" : "Unsaved"} ${id}`;
35582
- }));
35583
- if (result._tag === "Failure")
35584
- return formatError2(result.cause);
35585
- return formatSuccess(result.value);
35586
- });
35525
+ }, async ({ id, save }) => runTool(runtime4, exports_Effect.gen(function* () {
35526
+ const hasToken = yield* requireRefreshToken;
35527
+ if (!hasToken)
35528
+ return WRITE_AUTH_ERROR;
35529
+ const client = yield* RedditClient;
35530
+ const path = save ? "/api/save" : "/api/unsave";
35531
+ yield* client.post(path, { id });
35532
+ return `${save ? "Saved" : "Unsaved"} ${id}`;
35533
+ })));
35587
35534
  };
35588
35535
 
35589
35536
  // src/mcp/server.ts
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@daanrongen/reddit-mcp",
3
- "version": "1.0.1",
4
- "description": "MCP server for Reddit search posts, browse subreddits, read comments, and write with OAuth2 user auth over stdio",
3
+ "version": "1.1.0",
4
+ "description": "MCP server for Reddit \u2014 search posts, browse subreddits, read comments, and write with OAuth2 user auth over stdio",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "reddit-mcp": "./dist/main.js"
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "scripts": {
14
14
  "dev": "bun --watch src/main.ts",
15
- "inspect": "DANGEROUSLY_OMIT_AUTH=true mcp-inspector bun dist/main.js",
15
+ "inspect": "DANGEROUSLY_OMIT_AUTH=true mcp-inspector bun src/main.ts",
16
16
  "build": "bun build src/main.ts --outfile dist/main.js --target bun",
17
17
  "typecheck": "tsc -p tsconfig.check.json",
18
18
  "test": "bun test",