reddit-mcp-mod
# reddit-mcp-mod
A Model Context Protocol (MCP) stdio server exposing Reddit moderation as tools: reading posts/comments, posting/replying, reviewing the mod queue, approving/removing content, resolving reports, and managing users (ban/mute).
- **Entry point**: `src/index.ts` → compiled to `dist/index.js` (binary name: `reddit-mcp-mod`)
- **SDK**: `@modelcontextprotocol/sdk`
- **Node**: >= 24
- **Auth**: Reddit OAuth2 refresh-token flow (works with 2FA-enabled mod accounts; never stores your password)
## Setup
### 1. Create a Reddit app
Go to [reddit.com/prefs/apps](https://www.reddit.com/prefs/apps), click "create app", and choose type **web app** (not "script" — web apps get a `client_secret` and support permanent refresh tokens). Set the redirect URI to `http://localhost:65010/callback` (or a custom port, see `--port` below).
### 2. Install and build
```bash
pnpm install
pnpm build
```
### 3. Mint a refresh token (one-time)
```bash
node dist/index.js authorize \
--client_id <your_client_id> \
--client_secret <your_client_secret> \
--user_agent "windows:reddit-mcp-mod:0.1.0 (by /u/yourusername)"
```
This opens your browser, walks you through Reddit's consent screen, and prints a JSON block with `REDDIT_CLIENT_ID`, `REDDIT_CLIENT_SECRET`, `REDDIT_REFRESH_TOKEN`, and `REDDIT_USER_AGENT`. Save these as environment variables — the refresh token grants ongoing access to the account, so keep it secret.
### 4. Run the server
```bash
REDDIT_CLIENT_ID=... REDDIT_CLIENT_SECRET=... REDDIT_REFRESH_TOKEN=... REDDIT_USER_AGENT=... \
node dist/index.js --subreddits=mysubreddit
```
By default both `--allow_writes` and `--allow_mod_actions` are off, so the server starts read-only. Enable them once you've confirmed reads work.
## Configuration
### Auth
Supply `client_id`, `client_secret`, `refresh_token`, and `user_agent` via:
- Environment variables (recommended — keeps secrets out of `argv`): `REDDIT_CLIENT_ID`, `REDDIT_CLIENT_SECRET`, `REDDIT_REFRESH_TOKEN`, `REDDIT_USER_AGENT`
- CLI flags: `--client_id`, `--client_secret`, `--refresh_token`, `--user_agent`
- A `--profile <path.json>` file (see below)
`user_agent` should follow Reddit's recommended format `<platform>:<app id>:<version> (by /u/<username>)` — Reddit throttles non-compliant user agents. The server logs a warning at startup if the format doesn't match, but doesn't block startup.
### Write & moderation safety
Two independent gates, since posting content and banning users have very different blast radii:
- `--allow_writes` (default: false) — enables `reddit_submit_post`, `reddit_reply`, `reddit_edit_content`, `reddit_delete_content`.
- `--allow_mod_actions` (default: false) — enables `reddit_approve`, `reddit_remove`, `reddit_lock`, `reddit_unlock`, `reddit_ignore_reports`, `reddit_unignore_reports`, `reddit_ban_user`, `reddit_unban_user`, `reddit_mute_user`, `reddit_unmute_user`.
Gated tools are not registered at all when their flag is off (they won't appear in the MCP client's tool list), rather than being registered and erroring at call time.
### Subreddit allowlist
- `--subreddits sub1,sub2` — restricts every subreddit-scoped tool call to the listed subreddits (case-insensitive, `r/` prefix optional). Calls against any other subreddit are rejected before hitting Reddit's API.
- Default: `*` (unrestricted). The server logs a warning at startup when unrestricted.
### Flags & defaults
| Flag | Default | Description |
|---|---|---|
| `--allow_writes` | `false` | Enable posting/replying/editing/deleting |
| `--allow_mod_actions` | `false` | Enable approve/remove/lock/ban/mute/report actions |
| `--subreddits <a,b,c>` | `*` | Allowlist of subreddits the server may act on |
| `--timeout_ms <number>` | `15000` | Per-request timeout |
| `--log_level <silent\|error\|info\|debug>` | `info` | `debug` logs every HTTP request/response (secrets redacted) |
| `--profile <path.json>` | — | JSON file supplying any of the above (CLI flags and env vars take precedence) |
### Profile file
```json
{
"client_id": "<redacted>",
"client_secret": "<redacted>",
"refresh_token": "<redacted>",
"user_agent": "windows:reddit-mcp-mod:0.1.0 (by /u/yourusername)",
"allow_writes": false,
"allow_mod_actions": true,
"subreddits": ["mysubreddit"]
}
```
## Tools
**Reading** (always registered)
`reddit_get_post`, `reddit_get_comment`, `reddit_list_posts`, `reddit_search`, `reddit_get_user`, `reddit_list_user_activity`
**Posting** (requires `--allow_writes`)
`reddit_submit_post`, `reddit_reply`, `reddit_edit_content`, `reddit_delete_content`
**Mod queue** (listings always registered; actions require `--allow_mod_actions`)
`reddit_get_modqueue`, `reddit_get_unmoderated`, `reddit_get_spam`, `reddit_get_edited`, `reddit_approve`, `reddit_remove`, `reddit_lock`, `reddit_unlock`
**Reports** (`reddit_get_reports` always registered; actions require `--allow_mod_actions`)
`reddit_get_reports`, `reddit_ignore_reports`, `reddit_unignore_reports`
**Users** (list tools always registered; actions require `--allow_mod_actions`)
`reddit_ban_user`, `reddit_unban_user`, `reddit_mute_user`, `reddit_unmute_user`, `reddit_list_banned`, `reddit_list_muted`, `reddit_list_moderators`
**Mod log**
`reddit_get_modlog`
All tools return strict JSON (no Markdown) via a `content[0].text` payload, and use cursor-based pagination (`after`/`meta.next_cursor`) matching Reddit's own API, not offset-based pagination.
## Use in an MCP client (Claude Desktop / Claude Code)
```json
{
"mcpServers": {
"reddit-mod": {
"command": "node",
"args": ["/absolute/path/to/reddit-mcp-mod/dist/index.js", "--subreddits=mysubreddit"],
"env": {
"REDDIT_CLIENT_ID": "...",
"REDDIT_CLIENT_SECRET": "...",
"REDDIT_REFRESH_TOKEN": "...",
"REDDIT_USER_AGENT": "windows:reddit-mcp-mod:0.1.0 (by /u/yourusername)"
}
}
}
}
```
## Use with Open WebUI (via `mcpo`)
[`mcpo`](https://github.com/open-webui/mcpo) is Open WebUI's MCP-to-OpenAPI proxy. It's language-agnostic — it spawns any stdio MCP server as a subprocess and exposes an OpenAPI/HTTP surface on top, so this Node/TypeScript server works with it out of the box:
```bash
uvx mcpo --port 8000 -- node /absolute/path/to/reddit-mcp-mod/dist/index.js --subreddits=mysubreddit
```
Set the Reddit env vars (`REDDIT_CLIENT_ID`, `REDDIT_CLIENT_SECRET`, `REDDIT_REFRESH_TOKEN`, `REDDIT_USER_AGENT`) in the shell before running `mcpo`, or via an `mcpo` config file using the same `mcpServers` shape shown above. Once running, `http://localhost:8000/docs` lists one OpenAPI operation per tool — point Open WebUI's tool server settings at `http://localhost:8000`.
## Development
```bash
pnpm install
pnpm build # tsc -> dist/
pnpm typecheck # tsc --noEmit
pnpm lint # eslint src
pnpm test # node --test against dist/test/**/*.js (run pnpm build first)
```
Tests mock `RedditClient`/`fetch` directly — no live network calls or real Reddit credentials are required to run the suite.
## License
MIT
TDQS
Scored across 15 tools
Each tool targets a distinct resource or view: individual comments/posts, subreddit post listing, search, user profiles, user activity, and each moderation queue (modqueue, unmoderated, spam, edited, reports) with clear descriptions. No two tools appear to do the same thing.
All tools share the reddit_ prefix and follow a consistent verb_noun pattern: reddit_get_*, reddit_list_*, reddit_search. The naming style is uniform snake_case, making the set predictable and easy to navigate.
Fifteen tools is within the ideal 3-15 range and appropriate for a Reddit moderation server that covers content retrieval, user info, and various moderation queues. The number feels commensurate with the purpose.
The server is positioned as a moderation tool, yet it contains only read-only operations. The modqueue description explicitly references reddit_approve/reddit_remove actions that are not included, leaving agents unable to take any moderation action. This is a severe gap that undermines the server's core purpose.