redditapis-mcp 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0 (2026-09-11)
4
+
5
+ A minor rather than a patch because a tool was added: anyone pinned to 0.5.x opts in rather than receiving it silently, the same rule the 0.5.0 and 0.4.0 entries below state.
6
+
7
+ ### Added
8
+
9
+ - **`reddit_user_achievements`.** A user's public achievements, the trophies on their reddit.com/user/<name>/achievements page, with name, description, granted timestamp and icons. An account with none returns an empty list, so a zero count is a real answer.
10
+
11
+ ### Changed
12
+
13
+ - **`reddit_search` no longer steers you into site-wide `sort=top`.** Reddit orders `top`, `new` and `comments` by that one number over a LOOSELY matched set (OCR image text and comments count, and the word "reddit" is in almost every large post), so a generic multi-word query with `sort=top` and no `subreddit` returned the site-wide viral listing rather than the topic. Measured 2026-09-11: `q=reddit api pricing`, `sort=top`, `t=year` returned r/MadeMeSmile at 107k upvotes and a bathroom remodel; `sort=relevance` on the same query returned API-pricing threads; the quoted phrase returned exact matches only; `q=pgvector` was on topic on any sort. The tool description and the `sort` enum text now say only `relevance` weights match quality and point at `sort=relevance` plus `sort_type=score`, a quoted phrase, or a `subreddit` scope. The worked example no longer models `sort=top`.
14
+
15
+ ### Housekeeping
16
+
17
+ - The authoring repo had fallen behind the published package: 0.5.3 shipped from the org repo on 2026-09-06 without landing here, so `src/index.js` in this repo still exited on a missing key. That change and its test are now back in the authoring repo, and this release is cut from it.
18
+
19
+ ## 0.5.3 (2026-09-06)
20
+
21
+ ### Fixed
22
+
23
+ - **A registry scanner can now read the tool catalog without a key.** `src/index.js` used to `process.exit(1)` when `REDDITAPIS_KEY` was unset, so a directory that spawns the server to enumerate its tools got no answer to `initialize` and nothing from `tools/list`, and listed the server as uninspectable. Measured 2026-09-06 by speaking raw MCP stdio to the published 0.5.2 with an empty env. The server now warns and continues; a tool CALL with no key fails at the call with a clear message, and no `Bearer undefined` header is ever sent. Not a security relaxation: this is a stdio server, whoever spawns it already has local execution. Test: `test/scannable-without-key.test.mjs`. (This entry was written on 2026-09-11; the 0.5.3 release carried no changelog line.)
24
+
3
25
  ## 0.5.2 (2026-09-06)
4
26
 
5
27
  ### Added
package/README.md CHANGED
@@ -151,6 +151,7 @@ A few conventions across the catalog:
151
151
  | `reddit_user_profile` | `GET /api/reddit/user/{name}` | A user's public profile: karma, account age, verified/employee flags, avatar. |
152
152
  | `reddit_user_comments` | `GET /api/reddit/user/{name}/comments` | A user's recent comments (body, score, subreddit, parent link, timestamp). |
153
153
  | `reddit_user_submitted` | `GET /api/reddit/user/{name}/submitted` | A user's submitted posts (the sibling of `reddit_user_comments`). |
154
+ | `reddit_user_achievements` | `GET /api/reddit/user/{name}/achievements` | A user's public achievements (the trophies on their profile): name, description, granted timestamp, icons. An account with none returns an empty list. |
154
155
 
155
156
  ### Community browse (no keyword)
156
157
 
@@ -197,8 +198,9 @@ The agent calls `reddit_search` with:
197
198
 
198
199
  ```
199
200
  q: "borrow checker"
200
- sort: "top"
201
+ sort: "relevance"
201
202
  t: "month"
203
+ sort_type: "score"
202
204
  ```
203
205
 
204
206
  ### Read a community's top posts of the week
@@ -223,7 +225,7 @@ The agent calls `reddit_deep_comment_search` with:
223
225
  ```
224
226
  q: "mechanical keyboard"
225
227
  group_by: "author"
226
- sort: "top"
228
+ sort: "relevance"
227
229
  ```
228
230
 
229
231
  Research mode returns the distinct people who mentioned the query, ranked by how many of their comments matched, each with their top comment and the subreddits they matched in.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "redditapis-mcp",
3
- "version": "0.5.3",
3
+ "version": "0.6.0",
4
4
  "mcpName": "io.github.redditapis/redditapis-mcp",
5
5
  "description": "Official MCP server for redditapis.com. Search Reddit posts, comments, communities, users and media; read subreddits, comment trees, top posts and profiles; create and manage monitors and webhooks. Native tools for Claude, Cursor and any MCP client.",
6
6
  "repository": {
package/src/tools.js CHANGED
@@ -61,7 +61,7 @@ const SORT_POSTS = {
61
61
  };
62
62
  const SORT_SEARCH = {
63
63
  sort: z.enum(["relevance", "hot", "top", "new", "comments"]).optional().describe(
64
- "Sort order for search. 'relevance' = best match (default), 'top' = highest score in the `t` window, 'new' = most recent, 'hot' = trending, 'comments' = most-discussed.",
64
+ "Sort order for search. 'relevance' = best match (default), 'top' = highest score in the `t` window, 'new' = most recent, 'hot' = trending, 'comments' = most-discussed. Only 'relevance' weights how well a post matches; 'top', 'new' and 'comments' rank every loosely-matching post by that one number, so on a site-wide generic query 'top' returns viral posts that barely mention the terms. Prefer 'relevance', a quoted phrase, or a `subreddit` scope.",
65
65
  ),
66
66
  };
67
67
  const SORT_USER = {
@@ -265,7 +265,7 @@ export const TOOLS = [
265
265
  name: "reddit_search",
266
266
  path: "/api/reddit/search",
267
267
  description:
268
- "Search Reddit posts across all of Reddit or within one subreddit. Returns matching posts with author, score, comments, permalink, and an `after` cursor. Use for topic/keyword research, brand monitoring, or finding discussions. Scope to a community with `subreddit`. Optional advanced filters narrow the results by minimum/maximum score, comment count, media type, and post flags, with an optional re-sort of the page. Because filters are applied to the returned page, the response then carries a `meta` object with page-completeness counts, so a filtered result is never mistaken for the whole set; paginate with `after` to filter more. Example: q='rust vs go' sort='top' t='year'.",
268
+ "Search Reddit posts across all of Reddit or within one subreddit. Returns matching posts with author, score, comments, permalink, and an `after` cursor. Use for topic/keyword research, brand monitoring, or finding discussions. Scope to a community with `subreddit`. Optional advanced filters narrow the results by minimum/maximum score, comment count, media type, and post flags, with an optional re-sort of the page. Because filters are applied to the returned page, the response then carries a `meta` object with page-completeness counts, so a filtered result is never mistaken for the whole set; paginate with `after` to filter more. SORT CAVEAT, measured 2026-09-11: `top`, `new` and `comments` order the MATCHING set by score, date or comment count alone, and Reddit matches loosely (image OCR, comments, and the word 'reddit' is in almost every big post), so a generic multi-word query with sort='top' and no subreddit returns the site-wide viral listing, not the topic. Even a distinctive term is ranked by score alone (q='pgvector' sort='top': 1 of 5 results was about pgvector, the rest were large posts that mention it once). For best-match-then-highest-score, fetch with sort='relevance' and a large `limit`, then sort_type='score' re-orders that returned page (it never reaches past the page), or quote the phrase (q='\"rust vs go\"'), or scope with `subreddit`. Example: q='rust vs go' sort='relevance' t='year' limit=100 sort_type='score'.",
269
269
  shape: {
270
270
  ...QUERY,
271
271
  subreddit: z.string().optional().describe(
@@ -287,7 +287,7 @@ export const TOOLS = [
287
287
  stickied: z.boolean().optional().describe("Filter by the stickied flag."),
288
288
  spoiler: z.boolean().optional().describe("Filter by the spoiler flag."),
289
289
  contest_mode: z.boolean().optional().describe("Filter by the contest_mode flag."),
290
- sort_type: z.enum(["score", "num_comments", "created"]).optional().describe("Re-sort the filtered page (descending) by this field."),
290
+ sort_type: z.enum(["score", "num_comments", "created"]).optional().describe("Re-sort the filtered page (descending) by this field. Page-local: it re-orders only the posts this call returned, never the whole result set, so pair it with a large `limit`."),
291
291
  },
292
292
  },
293
293
  {
@@ -327,14 +327,14 @@ export const TOOLS = [
327
327
  name: "reddit_search_comments",
328
328
  path: "/api/reddit/search/comments",
329
329
  description:
330
- "Search Reddit by COMMENT text. Reddit's comment search matches your keyword against comment bodies but returns the PARENT POSTS, not the individual comments, so each result is a post whose discussion mentions your query, carrying that post's title, selftext, score, and comment count. Use it to surface threads where a topic comes up in the replies that plain post-title search would miss. Reddit does not expose which specific comment matched or its text, so this returns posts, not comment bodies. For the actual comment bodies, use reddit_deep_comment_search. Example: q='best mechanical keyboard' sort='top'.",
330
+ "Search Reddit by COMMENT text. Reddit's comment search matches your keyword against comment bodies but returns the PARENT POSTS, not the individual comments, so each result is a post whose discussion mentions your query, carrying that post's title, selftext, score, and comment count. Use it to surface threads where a topic comes up in the replies that plain post-title search would miss. Reddit does not expose which specific comment matched or its text, so this returns posts, not comment bodies. For the actual comment bodies, use reddit_deep_comment_search. Example: q='best mechanical keyboard' sort='relevance' t='year'.",
331
331
  shape: { ...QUERY, ...SORT_SEARCH, ...TIME_SEARCH, ...AFTER, ...NSFW, ...LIMIT },
332
332
  },
333
333
  {
334
334
  name: "reddit_deep_comment_search",
335
335
  path: "/api/reddit/search/comments/deep",
336
336
  description:
337
- "Genuine comment search: returns the ACTUAL comments whose body matches your keyword, sorted by score (highest first), with body, score, author, a comment-deep permalink, and the parent post. Unlike reddit_search_comments (which returns the parent posts, a Reddit limitation), this fetches each matching post's comment tree and filters the comment bodies for you, so you get first-hand opinions and answers directly. Premium call (it fans out into several reads): `limit` sets how many parent POSTS to expand, 1-25 (default 5), not how many comments come back. To go deeper than one call, paginate: pass the response's `after` cursor back as `after` to expand the NEXT batch of parent posts. `max_comments` optionally caps how many comments come back (the top-scored are kept). Matching is on the visible comment text at word boundaries (link URLs are ignored), so a result always mentions your query where a reader can see it. Best-effort: a deleted or deeply-nested comment may be missed (meta.truncated flags when a tree was too deep). Set group_by='author' for the research mode that returns WHO is talking about your query (distinct people ranked by matching-comment count) instead of a flat comment list, capped by max_authors. Example: q='best mechanical keyboard' sort='top'.",
337
+ "Genuine comment search: returns the ACTUAL comments whose body matches your keyword, sorted by score (highest first), with body, score, author, a comment-deep permalink, and the parent post. Unlike reddit_search_comments (which returns the parent posts, a Reddit limitation), this fetches each matching post's comment tree and filters the comment bodies for you, so you get first-hand opinions and answers directly. Premium call (it fans out into several reads): `limit` sets how many parent POSTS to expand, 1-25 (default 5), not how many comments come back. To go deeper than one call, paginate: pass the response's `after` cursor back as `after` to expand the NEXT batch of parent posts. `max_comments` optionally caps how many comments come back (the top-scored are kept). Matching is on the visible comment text at word boundaries (link URLs are ignored), so a result always mentions your query where a reader can see it. Best-effort: a deleted or deeply-nested comment may be missed (meta.truncated flags when a tree was too deep). Set group_by='author' for the research mode that returns WHO is talking about your query (distinct people ranked by matching-comment count) instead of a flat comment list, capped by max_authors. Example: q='best mechanical keyboard' sort='relevance' t='year'.",
338
338
  shape: {
339
339
  ...QUERY,
340
340
  ...SORT_SEARCH,
@@ -415,6 +415,17 @@ export const TOOLS = [
415
415
  ),
416
416
  },
417
417
  },
418
+ {
419
+ name: "reddit_user_achievements",
420
+ path: "/api/reddit/user/{name}/achievements",
421
+ description:
422
+ "List a Reddit user's public achievements, the trophies shown on their reddit.com/user/<name>/achievements page. Returns each achievement's name, description, granted timestamp and icons, plus a count. An account with none returns an empty list rather than an error, so a zero count is a real answer. Use to gauge account age and standing (One-Year Club, Verified Email) or to check a moderator's history. Example: name='spez'.",
423
+ shape: {
424
+ name: z.string().min(1).describe(
425
+ "Reddit username WITHOUT the u/ prefix (e.g. 'spez'). Required (path parameter).",
426
+ ),
427
+ },
428
+ },
418
429
  {
419
430
  name: "reddit_user_comments",
420
431
  path: "/api/reddit/user/{name}/comments",