reddit-radar-mcp

reddit-radar-mcp

Find Reddit threads where your product genuinely fits, reconstruct the conversation, and gate every drafted reply against a claim boundary you define.

Category
访问服务器

README

reddit-radar-mcp

Find Reddit threads where your product genuinely fits, reconstruct the conversation, and gate every drafted reply against a claim boundary you define.

Read-only by design. There is no code path that posts, votes, or acts as an account, and a test asserts there never will be. Drafts are for a human to review, edit, and post.

CI npm node license

npx reddit-radar-mcp          # run as an MCP server
npm install reddit-radar-mcp  # or use the scoring/gate functions directly

Requires Node 20.10+. No build step, no native dependencies.

Why this exists

The usual "social listening" tool finds mentions. That is the easy half. The hard half is everything after: is this thread actually relevant, what is the person really asking, and is the reply you are about to post true?

This package is built around three claims that came out of running it in production:

  1. Keyword matching produces mostly garbage. A recency + question-form + "any recommendations" heuristic scores 50/100 on literally any recent Reddit post. The fix is the anchor rule (below), and it is the most important thing here.
  2. Where a thread lives changes what you should say. The same question in a buyer subreddit and an engineering subreddit warrant different comments, so subreddit tiers set behaviour, not just ranking.
  3. A model writing promotional copy is the worst possible judge of whether it overclaimed. So the claim gate is deterministic, rule-based, and runs server-side. It refuses to hand back a blocked draft.

Quick start

Write a config:

Name it .mjs unless your project already sets "type": "module" — otherwise Node parses it as CommonJS and the import fails.

// radar.config.mjs
import { defineConfig, packs, composePacks } from 'reddit-radar-mcp';

export default defineConfig({
  product: {
    name: 'Acme',
    what: 'CI/CD pipeline observability.',
    claims: ['flaky test detection', 'build timing breakdowns'],
  },

  queries: ['flaky tests', 'CI pipeline slow', 'build times'],

  // REQUIRED. Without it, every recent question looks like an opportunity.
  domainTerms: ['ci', 'pipeline', 'flaky', 'github actions', 'test suite'],

  // Words that mean something else outside your niche.
  ambiguousTerms: ['build', 'runner'],

  tiers: {
    tier1: { mode: 'PROMOTE',        weight: 20, subreddits: ['devops'] },
    tier2: { mode: 'PROMOTE_SOFT',   weight: 15, subreddits: ['sre', 'kubernetes'] },
    tier3: { mode: 'CONTRIBUTE',     weight: 8,  subreddits: ['ExperiencedDevs'] },
    tier4: { mode: 'TECHNICAL_ONLY', weight: 3,  subreddits: ['programming'] },
  },

  gate: {
    ...composePacks(packs.noPricing, packs.noFabricatedMetrics, packs.noCustomerNames),
    productPattern: /\bAcme\b/i,
    unsupported: [
      { term: /\bJenkins\b/i, why: 'No Jenkins integration exists.' },
    ],
  },
});

Register it as an MCP server:

claude mcp add radar --scope user \
  -e RADAR_CONFIG=/abs/path/radar.config.mjs \
  -- npx reddit-radar-mcp

Then just talk to your agent: "run a sweep and show me what's worth replying to."

The anchor rule

The single most useful idea in this package.

A post is anchored only if something ties it to your domain: real domain vocabulary, an unambiguous query match, or a configured subreddit. Signals that describe the shape of a post — it is recent, it is a question, it says "recommendations" — can never carry a post on their own.

Without this gate, those shape signals sum to 40+ and pass anything. With it, a post in r/podcasts asking about a "POD episode" stops outranking a genuine buying question.

Two related behaviours fall out of the same idea:

  • Ambiguous terms ("build", "POD", "detention") only count when a second domain signal is present — or when the post is in one of your subreddits, since the subreddit is itself domain context.
  • Venting is penalized hard (-35). Rants out-engage buying questions, so without this the ranking inverts and you get "opportunities" that are people complaining about their coworkers.

Engagement modes

Tiers attach a mode to every result, and the sweep output repeats it next to each thread:

Mode Meaning
PROMOTE Name the product, describe the fitting capability, disclose affiliation.
PROMOTE_SOFT Answer first. Mention the product only if they are asking for tooling.
CONTRIBUTE Share insight. Product only as context for who you are.
TECHNICAL_ONLY Do not pitch. Nobody there is buying; promo gets removed.

The draft gate

check_draft runs two independent checks and refuses to return a blocked draft.

Claim gate (factCheck) — deterministic rules over your claim boundary. Starter packs cover the four common failure modes:

Pack Blocks
noPricing Dollar figures, per-unit rates, price-tier comparisons
noFabricatedMetrics Invented percentages, uptime/SLA claims, unverifiable scale
noCustomerNames Client references (even anonymous), case studies with measured outcomes
noMarketingSpeak "leverage", "seamless", "robust", "game-changing" (WARN)
requireDisclosure Naming your product without disclosing affiliation

Two behaviours worth knowing:

  • Denials are always allowed. "We do not support Jenkins" passes. An early version blocked it, which pushed drafts toward silence about gaps — the opposite of the intent. Conceding a real limitation is the cheapest credibility available.
  • Capability checks are assertion-scoped. "Jenkins is a solid choice if you need self-hosting" does not trip the gate, because it is not a claim about your product.

Quality gate (styleCheck) — catches text that reads as unedited generated filler: em dashes, semicolons, curly quotes, negation framing ("not just X, it's Y"), marketing vocabulary, flat sentence rhythm, and thin substance.

This is not AI-detection evasion. It cannot be and does not try to be. Many subreddits ban low-effort content, and moderators read comments rather than running classifiers. So the gate enforces what that rule actually asks for: real substance, no filler. The human still edits and posts, and disclosure is always present.

Pass your domain vocabulary so the substance check knows what a specific noun looks like:

styleCheck(draft, { anchorTerms: [...config.domainTerms, ...config.featureTerms] });

MCP tools

Tool Does LLM cost
plan_sweep Returns search URLs + the page extractor to run on each none
ingest_sweep Dedupes, scores, ranks swept results into an opportunity list none
score_thread 0–100 score for one post with per-point reasoning none
analyze_thread Reconstructs a thread + returns binding claim constraints none
parse_thread_html Same, from client-side browser extraction none
check_draft The enforcement point. APPROVED or BLOCKED none
get_claim_boundary What may and may not be claimed none

Every tool is deterministic. The model supplies the writing; the server supplies the facts and the veto.

Reddit access

Three interchangeable adapters behind one interface:

  • BrowserRedditClient — reads the same public pages a person reads, from your own browser tool. No credentials. This is the default path today.
  • RedditApiClient — OAuth against the official Data API. Access is approval-gated; see docs/REDDIT-ACCESS.md.
  • FixtureRedditClient — local JSON fixtures for tests and development.

Fixtures run through the same normalizers as live responses, so parsers are genuinely exercised rather than first meeting real data in production.

A caveat worth stating plainly: browser mode depends on Reddit's DOM, and Reddit ships redesigns. The extractors are written to fail loudly rather than silently return empty threads that look like "no discussion found."

Programmatic use

import { scoreRelevance, factCheck, styleCheck, packs, composePacks } from 'reddit-radar-mcp';
import config from './radar.config.js';

const result = scoreRelevance(post, config, { matchedQueries: ['flaky tests'] });
if (result.passed) console.log(result.score, result.reasons);

const gate = factCheck(draft, config.gate);
if (!gate.allowed) console.log(gate.findings);

Ethics and policy

This tool exists to help you find conversations you can genuinely contribute to. It will not help you astroturf.

  • No posting automation. Not implemented, and enforced by test.
  • Disclose affiliation. requireDisclosure is on by default. Undisclosed vendor comments get removed and can earn a permanent ban, which ends the channel entirely.
  • One account. Reddit's Responsible Builder Policy prohibits registering multiple accounts for the same use case. Do not use this to run a sockpuppet network.
  • Threads are scored, never people. Nothing here profiles an author, in line with Reddit's prohibition on inferring user characteristics.
  • Respect subreddit rules. TECHNICAL_ONLY exists because pitching in the wrong place is both rude and counterproductive.

Environment variables

Variable Default Purpose
RADAR_CONFIG — Required. Absolute path to your config (.js ESM with a default export, or .json).
REDDIT_MODE browser browser, live, or fixture. See docs/REDDIT-ACCESS.md.
REDDIT_CLIENT_ID — live mode only.
REDDIT_CLIENT_SECRET — live mode only.
REDDIT_USER_AGENT — live mode only. Must be <platform>:<appid>:<version> (by /u/<user>).
REDDIT_QPM 60 Rate limit for live mode. Lower than Reddit's advertised 100 on purpose.
RADAR_LOG_LEVEL info silent, error, warn, info, debug.
RADAR_LOG_FORMAT json json or text.

Full annotated list in .env.example.

Logs go to stderr only. On stdio transport stdout carries the JSON-RPC protocol, so anything written there corrupts the stream. Credentials in URLs and sensitive keys are redacted before logging.

Troubleshooting

Everything scores as an opportunity. Your domainTerms are too generic or missing. That list is what anchors a post to your domain, and without it the shape signals carry posts on their own. Config validation treats an empty list as an error for this reason.

Nothing scores at all. Check that domainTerms uses words that actually appear in post titles. Terms of 5+ characters match simple inflections (pipeline → pipelines); shorter ones match exactly, so app will not match apps.

A good draft is blocked as thin substance. Pass your vocabulary as anchorTerms — the MCP server does this from your config automatically, but a direct styleCheck() call needs it explicitly.

An honest limitation is blocked. It should not be; denials are explicitly allowed. Please report it.

Reddit shows "Prove your humanity". A cold search can hit a JS challenge. Loading any subreddit page first usually clears it for the session.

"Cannot use import statement outside a module". Your config is a .js file in a project without "type": "module", so Node parses it as CommonJS. Either name it radar.config.mjs or add "type": "module" to the nearest package.json. A .json config avoids the question entirely, at the cost of regex literals and composePacks.

More in SUPPORT.md.

Tests

npm test       # 33 unit tests
npm run smoke  # 14 checks over the real MCP wire protocol
npm run verify # everything, including the metadata consistency guard

The safety suite asserts that no client exposes a write method, no source file references a Reddit write endpoint, and the package exports no posting function.

Contributing

Issues and PRs welcome — see CONTRIBUTING.md. Note the permanent exclusions listed there: posting automation, multi-account support, and AI-detection evasion are deliberate non-goals rather than missing features.

Support this project

If this saves you time, sponsoring on GitHub helps keep it maintained. Entirely optional — the package is MIT and always will be.

Non-financial contributions are just as useful: a bug report with a reproducing config, a rule pack that generalizes, or a note about a scoring case that surprised you.

License

MIT — see LICENSE.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选