@citerocket/reddit-mcp 0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CiteRocket
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # Reddit MCP: the Reddit API for Claude, Cursor and any MCP client
2
+
3
+ [![npm](https://img.shields.io/npm/v/@citerocket/reddit-mcp.svg)](https://www.npmjs.com/package/@citerocket/reddit-mcp)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE)
5
+
6
+ The official **[CiteRocket](https://www.citerocket.com)** MCP server, the fastest way
7
+ to use the **Reddit API** from an AI assistant. Give Claude, Cursor, or any
8
+ [Model Context Protocol](https://modelcontextprotocol.io) client direct access to
9
+ Reddit data: **search Reddit posts and comments, pull a subreddit's top, read full
10
+ comment trees, look up users and their karma, search communities, media and users,
11
+ and run shadowban checks**, all over one clean, fast, camelCase JSON API.
12
+
13
+ **Tools stay current automatically.** When CiteRocket ships a new endpoint, the new
14
+ tool shows up on your next restart, with no reinstall and no config change.
15
+
16
+ → **[Get an API key at citerocket.com](https://app.citerocket.com)** · **[API docs](https://www.citerocket.com/docs)** · **[Rate limits](https://www.citerocket.com/docs/rate-limits)**
17
+
18
+ ---
19
+
20
+ ## Install
21
+
22
+ Add it to your MCP client config (Claude Desktop, Cursor, Windsurf, etc.):
23
+
24
+ ```json
25
+ {
26
+ "mcpServers": {
27
+ "citerocket": {
28
+ "command": "npx",
29
+ "args": ["-y", "@citerocket/reddit-mcp@latest"],
30
+ "env": {
31
+ "CITEROCKET_API_KEY": "cite_live_..."
32
+ }
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ Get your `CITEROCKET_API_KEY` from the [CiteRocket dashboard](https://app.citerocket.com).
39
+ That's all it needs.
40
+
41
+ Or via the Claude Code CLI:
42
+
43
+ ```bash
44
+ claude mcp add citerocket -e CITEROCKET_API_KEY=cite_live_... -- npx -y @citerocket/reddit-mcp@latest
45
+ ```
46
+
47
+ ---
48
+
49
+ ## What you can do
50
+
51
+ Once installed, just ask your assistant naturally:
52
+
53
+ > "Find the top posts in r/programming this week"
54
+ > "Search Reddit for posts about the new iPhone, sorted by top"
55
+ > "What's u/spez's karma and account age?"
56
+ > "Is u/some_account shadowbanned or suspended?"
57
+ > "Get the full comment tree for this post: https://reddit.com/r/..."
58
+ > "Search r/startups comments that mention 'pricing'"
59
+ > "Show me the rules and moderators of r/webdev"
60
+
61
+ ---
62
+
63
+ ## Tools
64
+
65
+ All tools are read-only. Query parameters are `snake_case`; responses are
66
+ camelCase JSON.
67
+
68
+ | Tool | What it does |
69
+ |------|--------------|
70
+ | `get_posts` | Fetch posts from a subreddit |
71
+ | `get_search` | Search posts across Reddit |
72
+ | `get_comments` | A post's full comment tree |
73
+ | `get_post_by_id` | Fetch one post by id |
74
+ | `get_sub_by_name_top` | A subreddit's top posts |
75
+ | `get_sub_by_name_info` | Subreddit metadata, rules, moderators |
76
+ | `get_user_by_name` | User profile and karma |
77
+ | `get_user_by_name_comments` | A user's recent comments |
78
+ | `get_user_by_name_status` | Account status / shadowban check |
79
+ | `get_search_communities` | Search communities |
80
+ | `get_search_comments` | Search comments |
81
+ | `get_search_media` | Search media |
82
+ | `get_search_users` | Search users |
83
+ | `get_cite_rocket_overview` | Your credit, usage, and billing snapshot |
84
+
85
+ The live set is whatever the server pulls from the manifest at startup, so this
86
+ table tracks what's shipped today, not a hardcoded ceiling.
87
+
88
+ ---
89
+
90
+ ## How billing works
91
+
92
+ Every tool call is a normal CiteRocket API call authenticated with your
93
+ `CITEROCKET_API_KEY`, metered exactly like direct API usage, no separate MCP
94
+ billing. Standard reads cost 1 credit; `get_cite_rocket_overview` is free.
95
+ Per-plan QPS ceilings apply, see the [rate limits](https://www.citerocket.com/docs/rate-limits).
96
+
97
+ ---
98
+
99
+ ## Configuration
100
+
101
+ | Env var | Required | Purpose |
102
+ |---------|----------|---------|
103
+ | `CITEROCKET_API_KEY` | yes | Your API key, sent as `Authorization: Bearer` on every call. |
104
+
105
+ If CiteRocket is briefly unreachable at startup, the server falls back to the tool
106
+ set bundled in the package so your existing tools keep working.
107
+
108
+ ---
109
+
110
+ ## Development
111
+
112
+ ```bash
113
+ npm install
114
+ npm run build:manifest # regenerate the bundled tool manifest from the OpenAPI spec
115
+ npm run build # compile TypeScript to dist/
116
+ ```
117
+
118
+ ---
119
+
120
+ ## Links
121
+
122
+ - 🌐 **Website:** [citerocket.com](https://www.citerocket.com)
123
+ - 📚 **API docs:** [citerocket.com/docs](https://www.citerocket.com/docs)
124
+ - 🔑 **Get an API key:** [app.citerocket.com](https://app.citerocket.com)
125
+ - 📈 **Status:** [citerocket.com/status](https://www.citerocket.com/status)
126
+ - ✉️ **Support:** [support@citerocket.com](mailto:support@citerocket.com)
127
+
128
+ CiteRocket is an independent third-party API for developers and researchers. Not
129
+ affiliated with, endorsed by, or sponsored by Reddit, Inc.
130
+
131
+ ## License
132
+
133
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CiteRocket MCP server — thin, manifest-driven runtime.
4
+ *
5
+ * Holds ZERO hardcoded endpoints. On startup it loads the tool manifest
6
+ * (fetched from api.citerocket.com/mcp/manifest, with the bundled manifest.json
7
+ * as an offline fallback) and registers every tool dynamically. A single
8
+ * generic handler proxies each call to api.citerocket.com.
9
+ *
10
+ * This is what lets new endpoints reach every user WITHOUT a reinstall: add the
11
+ * endpoint to the API's OpenAPI spec, rebuild + deploy the API (which serves the
12
+ * updated manifest), and every already-installed MCP picks up the new tools on
13
+ * its next startup. No `npm update`, no config change.
14
+ *
15
+ * Auth model:
16
+ * CITEROCKET_API_KEY — required. Sent as `Authorization: Bearer` on every
17
+ * call. This is the only credential; the current tool surface is read-only.
18
+ */
19
+ import { readFileSync } from "node:fs";
20
+ import { fileURLToPath } from "node:url";
21
+ import path from "node:path";
22
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
23
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
24
+ import { ListToolsRequestSchema, CallToolRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
25
+ // ── Config ────────────────────────────────────────────────────────────────
26
+ const API_KEY = process.env.CITEROCKET_API_KEY || process.env.CITEROCKET_KEY;
27
+ const MANIFEST_URL = process.env.CITEROCKET_MANIFEST_URL ||
28
+ "https://api.citerocket.com/mcp/manifest";
29
+ if (!API_KEY) {
30
+ console.error("[reddit-mcp] Missing CITEROCKET_API_KEY. Set it in your MCP config:\n" +
31
+ ' "env": { "CITEROCKET_API_KEY": "cite_live_..." }\n' +
32
+ "Get a key at https://app.citerocket.com");
33
+ process.exit(1);
34
+ }
35
+ // ── Manifest loading: hosted URL, bundled fallback ──────────────────────────
36
+ async function loadManifest() {
37
+ try {
38
+ const res = await fetch(MANIFEST_URL, { signal: AbortSignal.timeout(8000) });
39
+ if (res.ok)
40
+ return (await res.json());
41
+ console.error(`[reddit-mcp] manifest fetch ${res.status}; using bundled copy`);
42
+ }
43
+ catch (err) {
44
+ console.error(`[reddit-mcp] manifest fetch failed (${err.message}); using bundled copy`);
45
+ }
46
+ // Offline fallback: the manifest bundled in the npm tarball. If both the live
47
+ // fetch and the bundle are unavailable, fail loudly — since tool calls hit the
48
+ // same host, an unreachable manifest means the API itself is unreachable.
49
+ try {
50
+ const here = path.dirname(fileURLToPath(import.meta.url));
51
+ const bundled = path.resolve(here, "../manifest.json");
52
+ return JSON.parse(readFileSync(bundled, "utf8"));
53
+ }
54
+ catch {
55
+ throw new Error(`Could not load the tool manifest from ${MANIFEST_URL}, and no bundled ` +
56
+ `fallback was found. Check your network and that api.citerocket.com is reachable.`);
57
+ }
58
+ }
59
+ // ── The generic call handler ────────────────────────────────────────────────
60
+ async function callEndpoint(manifest, tool, args) {
61
+ const query = {};
62
+ const body = {};
63
+ let urlPath = tool.path;
64
+ for (const [field, where] of Object.entries(tool.placement)) {
65
+ const value = args[field];
66
+ if (value === undefined || value === null || value === "")
67
+ continue;
68
+ if (where === "query")
69
+ query[field] = String(value);
70
+ else if (where === "path")
71
+ urlPath = urlPath.replace(`{${field}}`, encodeURIComponent(String(value)));
72
+ else
73
+ body[field] = value;
74
+ }
75
+ const qs = Object.keys(query).length ? "?" + new URLSearchParams(query) : "";
76
+ const url = `${manifest.server}${urlPath}${qs}`;
77
+ const res = await fetch(url, {
78
+ method: tool.method,
79
+ headers: {
80
+ Authorization: `Bearer ${API_KEY}`,
81
+ "Content-Type": "application/json",
82
+ },
83
+ body: tool.method !== "GET" ? JSON.stringify(body) : undefined,
84
+ });
85
+ const text = await res.text();
86
+ return { isError: !res.ok, content: [{ type: "text", text }] };
87
+ }
88
+ // ── Boot ────────────────────────────────────────────────────────────────────
89
+ async function main() {
90
+ const manifest = await loadManifest();
91
+ const byName = new Map(manifest.tools.map((t) => [t.name, t]));
92
+ const server = new Server({ name: "citerocket", version: "0.1.0" }, { capabilities: { tools: {} } });
93
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
94
+ tools: manifest.tools.map((t) => ({
95
+ name: t.name,
96
+ description: t.description,
97
+ inputSchema: t.inputSchema,
98
+ ...(t.annotations ? { annotations: t.annotations } : {}),
99
+ })),
100
+ }));
101
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
102
+ const tool = byName.get(req.params.name);
103
+ if (!tool) {
104
+ return {
105
+ isError: true,
106
+ content: [{ type: "text", text: `Unknown tool: ${req.params.name}` }],
107
+ };
108
+ }
109
+ const args = (req.params.arguments || {});
110
+ try {
111
+ return await callEndpoint(manifest, tool, args);
112
+ }
113
+ catch (err) {
114
+ return {
115
+ isError: true,
116
+ content: [{ type: "text", text: `Error: ${err.message}` }],
117
+ };
118
+ }
119
+ });
120
+ await server.connect(new StdioServerTransport());
121
+ console.error(`[reddit-mcp] ready — ${manifest.tools.length} tools (manifest ${manifest.version})`);
122
+ }
123
+ main().catch((err) => {
124
+ console.error("[reddit-mcp] fatal:", err);
125
+ process.exit(1);
126
+ });
package/manifest.json ADDED
@@ -0,0 +1,588 @@
1
+ {
2
+ "version": "2026-09-12T12:44:46.936Z",
3
+ "server": "https://api.citerocket.com",
4
+ "tools": [
5
+ {
6
+ "name": "get_posts",
7
+ "description": "Fetch posts from a subreddit",
8
+ "method": "GET",
9
+ "path": "/api/reddit/posts",
10
+ "annotations": {
11
+ "readOnlyHint": true
12
+ },
13
+ "placement": {
14
+ "subreddit": "query",
15
+ "sort": "query",
16
+ "t": "query",
17
+ "limit": "query",
18
+ "after": "query"
19
+ },
20
+ "inputSchema": {
21
+ "type": "object",
22
+ "properties": {
23
+ "subreddit": {
24
+ "type": "string",
25
+ "description": "Subreddit name (no r/ prefix)"
26
+ },
27
+ "sort": {
28
+ "type": "string",
29
+ "enum": [
30
+ "new",
31
+ "hot",
32
+ "top",
33
+ "rising",
34
+ "controversial",
35
+ "best"
36
+ ],
37
+ "default": "new",
38
+ "description": "Sort tab"
39
+ },
40
+ "t": {
41
+ "type": "string",
42
+ "enum": [
43
+ "hour",
44
+ "day",
45
+ "week",
46
+ "month",
47
+ "year",
48
+ "all"
49
+ ],
50
+ "description": "Timeframe for sort=top/controversial"
51
+ },
52
+ "limit": {
53
+ "type": "integer",
54
+ "default": 25,
55
+ "description": "Items to return, 1-100"
56
+ },
57
+ "after": {
58
+ "type": "string",
59
+ "description": "Pagination cursor from a previous response"
60
+ }
61
+ },
62
+ "required": [
63
+ "subreddit"
64
+ ]
65
+ }
66
+ },
67
+ {
68
+ "name": "get_search",
69
+ "description": "Search posts across Reddit",
70
+ "method": "GET",
71
+ "path": "/api/reddit/search",
72
+ "annotations": {
73
+ "readOnlyHint": true
74
+ },
75
+ "placement": {
76
+ "q": "query",
77
+ "subreddit": "query",
78
+ "sort": "query",
79
+ "t": "query",
80
+ "nsfw": "query",
81
+ "limit": "query",
82
+ "after": "query"
83
+ },
84
+ "inputSchema": {
85
+ "type": "object",
86
+ "properties": {
87
+ "q": {
88
+ "type": "string",
89
+ "description": "Search query"
90
+ },
91
+ "subreddit": {
92
+ "type": "string",
93
+ "description": "Restrict to a subreddit"
94
+ },
95
+ "sort": {
96
+ "type": "string",
97
+ "enum": [
98
+ "relevance",
99
+ "new",
100
+ "hot",
101
+ "top",
102
+ "comments"
103
+ ],
104
+ "description": "Sort order"
105
+ },
106
+ "t": {
107
+ "type": "string",
108
+ "enum": [
109
+ "hour",
110
+ "day",
111
+ "week",
112
+ "month",
113
+ "year",
114
+ "all"
115
+ ],
116
+ "description": "Timeframe"
117
+ },
118
+ "nsfw": {
119
+ "type": "boolean",
120
+ "description": "true to include NSFW results"
121
+ },
122
+ "limit": {
123
+ "type": "integer",
124
+ "default": 25,
125
+ "description": "Items to return, 1-100"
126
+ },
127
+ "after": {
128
+ "type": "string",
129
+ "description": "Pagination cursor from a previous response"
130
+ }
131
+ },
132
+ "required": [
133
+ "q"
134
+ ]
135
+ }
136
+ },
137
+ {
138
+ "name": "get_sub_by_name_top",
139
+ "description": "A subreddit's top posts",
140
+ "method": "GET",
141
+ "path": "/api/reddit/sub/{name}/top",
142
+ "annotations": {
143
+ "readOnlyHint": true
144
+ },
145
+ "placement": {
146
+ "name": "path",
147
+ "t": "query",
148
+ "limit": "query",
149
+ "after": "query"
150
+ },
151
+ "inputSchema": {
152
+ "type": "object",
153
+ "properties": {
154
+ "name": {
155
+ "type": "string",
156
+ "description": "Subreddit name (no r/ prefix)"
157
+ },
158
+ "t": {
159
+ "type": "string",
160
+ "enum": [
161
+ "day",
162
+ "week",
163
+ "month",
164
+ "year",
165
+ "all"
166
+ ],
167
+ "default": "week",
168
+ "description": "Timeframe"
169
+ },
170
+ "limit": {
171
+ "type": "integer",
172
+ "default": 25,
173
+ "description": "Items to return, 1-100"
174
+ },
175
+ "after": {
176
+ "type": "string",
177
+ "description": "Pagination cursor from a previous response"
178
+ }
179
+ },
180
+ "required": [
181
+ "name"
182
+ ]
183
+ }
184
+ },
185
+ {
186
+ "name": "get_sub_by_name_info",
187
+ "description": "Subreddit metadata, rules, moderators",
188
+ "method": "GET",
189
+ "path": "/api/reddit/sub/{name}/info",
190
+ "annotations": {
191
+ "readOnlyHint": true
192
+ },
193
+ "placement": {
194
+ "name": "path"
195
+ },
196
+ "inputSchema": {
197
+ "type": "object",
198
+ "properties": {
199
+ "name": {
200
+ "type": "string",
201
+ "description": "Subreddit name (no r/ prefix)"
202
+ }
203
+ },
204
+ "required": [
205
+ "name"
206
+ ]
207
+ }
208
+ },
209
+ {
210
+ "name": "get_comments",
211
+ "description": "A post's full comment tree",
212
+ "method": "GET",
213
+ "path": "/api/reddit/comments",
214
+ "annotations": {
215
+ "readOnlyHint": true
216
+ },
217
+ "placement": {
218
+ "permalink": "query"
219
+ },
220
+ "inputSchema": {
221
+ "type": "object",
222
+ "properties": {
223
+ "permalink": {
224
+ "type": "string",
225
+ "description": "Post permalink, e.g. /r/X/comments/abc/title/"
226
+ }
227
+ },
228
+ "required": [
229
+ "permalink"
230
+ ]
231
+ }
232
+ },
233
+ {
234
+ "name": "get_post_by_id",
235
+ "description": "Fetch one post by id",
236
+ "method": "GET",
237
+ "path": "/api/reddit/post/{id}",
238
+ "annotations": {
239
+ "readOnlyHint": true
240
+ },
241
+ "placement": {
242
+ "id": "path"
243
+ },
244
+ "inputSchema": {
245
+ "type": "object",
246
+ "properties": {
247
+ "id": {
248
+ "type": "string",
249
+ "description": "Reddit post id, e.g. 1sgjld3"
250
+ }
251
+ },
252
+ "required": [
253
+ "id"
254
+ ]
255
+ }
256
+ },
257
+ {
258
+ "name": "get_user_by_name",
259
+ "description": "User profile and karma",
260
+ "method": "GET",
261
+ "path": "/api/reddit/user/{name}",
262
+ "annotations": {
263
+ "readOnlyHint": true
264
+ },
265
+ "placement": {
266
+ "name": "path"
267
+ },
268
+ "inputSchema": {
269
+ "type": "object",
270
+ "properties": {
271
+ "name": {
272
+ "type": "string",
273
+ "description": "Reddit username (no u/ prefix)"
274
+ }
275
+ },
276
+ "required": [
277
+ "name"
278
+ ]
279
+ }
280
+ },
281
+ {
282
+ "name": "get_user_by_name_comments",
283
+ "description": "A user's recent comments",
284
+ "method": "GET",
285
+ "path": "/api/reddit/user/{name}/comments",
286
+ "annotations": {
287
+ "readOnlyHint": true
288
+ },
289
+ "placement": {
290
+ "name": "path",
291
+ "sort": "query",
292
+ "limit": "query",
293
+ "after": "query"
294
+ },
295
+ "inputSchema": {
296
+ "type": "object",
297
+ "properties": {
298
+ "name": {
299
+ "type": "string",
300
+ "description": "Reddit username"
301
+ },
302
+ "sort": {
303
+ "type": "string",
304
+ "enum": [
305
+ "new",
306
+ "top",
307
+ "controversial"
308
+ ],
309
+ "default": "new",
310
+ "description": "Sort order"
311
+ },
312
+ "limit": {
313
+ "type": "integer",
314
+ "default": 25,
315
+ "description": "Items to return, 1-100"
316
+ },
317
+ "after": {
318
+ "type": "string",
319
+ "description": "Pagination cursor from a previous response"
320
+ }
321
+ },
322
+ "required": [
323
+ "name"
324
+ ]
325
+ }
326
+ },
327
+ {
328
+ "name": "get_user_by_name_status",
329
+ "description": "Account status / shadowban check",
330
+ "method": "GET",
331
+ "path": "/api/reddit/user/{name}/status",
332
+ "annotations": {
333
+ "readOnlyHint": true
334
+ },
335
+ "placement": {
336
+ "name": "path"
337
+ },
338
+ "inputSchema": {
339
+ "type": "object",
340
+ "properties": {
341
+ "name": {
342
+ "type": "string",
343
+ "description": "Reddit username (no u/ prefix)"
344
+ }
345
+ },
346
+ "required": [
347
+ "name"
348
+ ]
349
+ }
350
+ },
351
+ {
352
+ "name": "get_search_communities",
353
+ "description": "Search communities",
354
+ "method": "GET",
355
+ "path": "/api/reddit/search/communities",
356
+ "annotations": {
357
+ "readOnlyHint": true
358
+ },
359
+ "placement": {
360
+ "q": "query",
361
+ "nsfw": "query",
362
+ "limit": "query",
363
+ "after": "query"
364
+ },
365
+ "inputSchema": {
366
+ "type": "object",
367
+ "properties": {
368
+ "q": {
369
+ "type": "string",
370
+ "description": "Search query"
371
+ },
372
+ "nsfw": {
373
+ "type": "boolean",
374
+ "description": "true to include NSFW results"
375
+ },
376
+ "limit": {
377
+ "type": "integer",
378
+ "default": 25,
379
+ "description": "Items to return, 1-100"
380
+ },
381
+ "after": {
382
+ "type": "string",
383
+ "description": "Pagination cursor from a previous response"
384
+ }
385
+ },
386
+ "required": [
387
+ "q"
388
+ ]
389
+ }
390
+ },
391
+ {
392
+ "name": "get_search_comments",
393
+ "description": "Search comments",
394
+ "method": "GET",
395
+ "path": "/api/reddit/search/comments",
396
+ "annotations": {
397
+ "readOnlyHint": true
398
+ },
399
+ "placement": {
400
+ "q": "query",
401
+ "sort": "query",
402
+ "t": "query",
403
+ "nsfw": "query",
404
+ "limit": "query",
405
+ "after": "query"
406
+ },
407
+ "inputSchema": {
408
+ "type": "object",
409
+ "properties": {
410
+ "q": {
411
+ "type": "string",
412
+ "description": "Search query"
413
+ },
414
+ "sort": {
415
+ "type": "string",
416
+ "enum": [
417
+ "relevance",
418
+ "new",
419
+ "top"
420
+ ],
421
+ "default": "relevance",
422
+ "description": "Sort order"
423
+ },
424
+ "t": {
425
+ "type": "string",
426
+ "enum": [
427
+ "hour",
428
+ "day",
429
+ "week",
430
+ "month",
431
+ "year",
432
+ "all"
433
+ ],
434
+ "description": "Timeframe"
435
+ },
436
+ "nsfw": {
437
+ "type": "boolean",
438
+ "description": "true to include NSFW results"
439
+ },
440
+ "limit": {
441
+ "type": "integer",
442
+ "default": 25,
443
+ "description": "Items to return, 1-100"
444
+ },
445
+ "after": {
446
+ "type": "string",
447
+ "description": "Pagination cursor from a previous response"
448
+ }
449
+ },
450
+ "required": [
451
+ "q"
452
+ ]
453
+ }
454
+ },
455
+ {
456
+ "name": "get_search_media",
457
+ "description": "Search media",
458
+ "method": "GET",
459
+ "path": "/api/reddit/search/media",
460
+ "annotations": {
461
+ "readOnlyHint": true
462
+ },
463
+ "placement": {
464
+ "q": "query",
465
+ "kind": "query",
466
+ "sort": "query",
467
+ "t": "query",
468
+ "nsfw": "query",
469
+ "limit": "query",
470
+ "after": "query"
471
+ },
472
+ "inputSchema": {
473
+ "type": "object",
474
+ "properties": {
475
+ "q": {
476
+ "type": "string",
477
+ "description": "Search query"
478
+ },
479
+ "kind": {
480
+ "type": "string",
481
+ "enum": [
482
+ "image",
483
+ "video",
484
+ "gallery",
485
+ "all"
486
+ ],
487
+ "default": "all",
488
+ "description": "Media kind"
489
+ },
490
+ "sort": {
491
+ "type": "string",
492
+ "enum": [
493
+ "relevance",
494
+ "new",
495
+ "hot",
496
+ "top",
497
+ "comments"
498
+ ],
499
+ "default": "relevance",
500
+ "description": "Sort order"
501
+ },
502
+ "t": {
503
+ "type": "string",
504
+ "enum": [
505
+ "hour",
506
+ "day",
507
+ "week",
508
+ "month",
509
+ "year",
510
+ "all"
511
+ ],
512
+ "description": "Timeframe"
513
+ },
514
+ "nsfw": {
515
+ "type": "boolean",
516
+ "description": "true to include NSFW results"
517
+ },
518
+ "limit": {
519
+ "type": "integer",
520
+ "default": 25,
521
+ "description": "Items to return, 1-100"
522
+ },
523
+ "after": {
524
+ "type": "string",
525
+ "description": "Pagination cursor from a previous response"
526
+ }
527
+ },
528
+ "required": [
529
+ "q"
530
+ ]
531
+ }
532
+ },
533
+ {
534
+ "name": "get_search_users",
535
+ "description": "Search users",
536
+ "method": "GET",
537
+ "path": "/api/reddit/search/users",
538
+ "annotations": {
539
+ "readOnlyHint": true
540
+ },
541
+ "placement": {
542
+ "q": "query",
543
+ "nsfw": "query",
544
+ "limit": "query",
545
+ "after": "query"
546
+ },
547
+ "inputSchema": {
548
+ "type": "object",
549
+ "properties": {
550
+ "q": {
551
+ "type": "string",
552
+ "description": "Search query"
553
+ },
554
+ "nsfw": {
555
+ "type": "boolean",
556
+ "description": "true to include NSFW results"
557
+ },
558
+ "limit": {
559
+ "type": "integer",
560
+ "default": 25,
561
+ "description": "Items to return, 1-100"
562
+ },
563
+ "after": {
564
+ "type": "string",
565
+ "description": "Pagination cursor from a previous response"
566
+ }
567
+ },
568
+ "required": [
569
+ "q"
570
+ ]
571
+ }
572
+ },
573
+ {
574
+ "name": "get_cite_rocket_overview",
575
+ "description": "Return member identity, plan access, spendable credits, lifetime usage and billing history in one free request",
576
+ "method": "GET",
577
+ "path": "/citerocket/overview",
578
+ "annotations": {
579
+ "readOnlyHint": true
580
+ },
581
+ "placement": {},
582
+ "inputSchema": {
583
+ "type": "object",
584
+ "properties": {}
585
+ }
586
+ }
587
+ ]
588
+ }
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@citerocket/reddit-mcp",
3
+ "version": "0.1.0",
4
+ "description": "Official Reddit API MCP server by CiteRocket — search Reddit posts & comments, subreddit top, comment trees, user karma, communities, media, and shadowban checks inside Claude, Cursor, Windsurf, and any MCP client.",
5
+ "type": "module",
6
+ "bin": {
7
+ "reddit-mcp": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "manifest.json",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "build:manifest": "node scripts/build-manifest.mjs",
17
+ "build": "tsc",
18
+ "prepublishOnly": "npm run build:manifest && npm run build"
19
+ },
20
+ "keywords": [
21
+ "mcp",
22
+ "model-context-protocol",
23
+ "reddit",
24
+ "reddit-api",
25
+ "claude",
26
+ "cursor",
27
+ "citerocket"
28
+ ],
29
+ "homepage": "https://www.citerocket.com",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/citerocket-hq/reddit-mcp.git"
33
+ },
34
+ "license": "MIT",
35
+ "dependencies": {
36
+ "@modelcontextprotocol/sdk": "^1.0.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^22.0.0",
40
+ "typescript": "^5.5.0"
41
+ }
42
+ }