trend-pulse
Agentic Trend Intelligence Platform — 37 sources, plugin system, vector search, lifecycle prediction, 6-agent content factory, web dashboard, and 29-tool MCP server.
Use as a Python library, CLI tool, MCP server for Claude Code / AI agents, or a standalone web dashboard.
One-line MCP setup (zero install):
{ "mcpServers": { "trend-pulse": { "command": "uvx", "args": ["--from", "trend-pulse[mcp]", "trend-pulse-server"], "type": "stdio" } } }
Paste into
.mcp.jsonand you're done. Requires uv (brew install uvorcurl -LsSf https://astral.sh/uv/install.sh | sh).
Sources
Built-in Sources (20)
All built-in sources are free and require zero authentication. Run trend-pulse sources to list them, or the get_source_status MCP tool to see which ones are reachable from your network:
Plugin Sources (17)
Plugin sources live in src/trend_pulse/plugins/sources/ and are auto-discovered at startup:
Sources marked "returns nothing" sit behind login walls or bot checks that trend-pulse does not try to get around. They return an empty list instead of junk, and start working again if the site reopens public access.
Install
Zero-install with uvx (recommended)
uvx runs Python packages directly — no install, no venv, no setup:
# Run the CLI instantly
uvx trend-pulse trending
# Run the MCP server
uvx --from "trend-pulse[mcp]" trend-pulse-server
uvxis the Python equivalent ofnpx. It comes with uv — install uv withcurl -LsSf https://astral.sh/uv/install.sh | sh
pip install
pip install trend-pulse # core (httpx + aiosqlite)
pip install "trend-pulse[mcp]" # MCP server
pip install "trend-pulse[dashboard]" # Streamlit + FastAPI
pip install "trend-pulse[llm]" # Claude API for hybrid scoring
pip install "trend-pulse[social]" # twikit for X/Twitter trends
pip install "trend-pulse[all]" # everything
Quick Start
CLI
# What's trending right now? (all 37 sources, merged ranking)
trend-pulse trending
# Taiwan trends from Google + Hacker News
trend-pulse trending --sources google_trends,hackernews --geo TW
# Fetch + save snapshot to history DB
trend-pulse trending --save --count 10
# Take a full snapshot (all sources, auto-saves)
trend-pulse snapshot
# Query historical trends for a keyword
trend-pulse history "Claude" --days 7
# Search across sources
trend-pulse search "AI agent"
# List available sources (built-in + plugins)
trend-pulse sources
Python
import asyncio
from trend_pulse.aggregator import TrendAggregator
async def main():
agg = TrendAggregator()
# All sources, merged ranking
result = await agg.trending(geo="TW", count=10)
for item in result["merged"]:
print(f"[{item['source']}] {item['keyword']} ({item.get('traffic', '')})")
# With snapshot saving + velocity enrichment
result = await agg.trending(count=5, save=True)
for item in result["merged"]:
print(f"{item['keyword']} — {item['direction']} (velocity: {item['velocity']})")
# Query history
history = await agg.history("Claude", days=7)
for record in history["records"]:
print(f" {record['timestamp']}: score={record['score']}")
# Search
result = await agg.search("Claude AI")
for item in result["merged"][:5]:
print(f"{item['keyword']} - {item['score']:.0f}")
asyncio.run(main())
Single Source
import asyncio
from trend_pulse.sources import HackerNewsSource
async def main():
hn = HackerNewsSource()
items = await hn.fetch_trending(count=5)
for item in items:
print(f"{item.keyword} ({item.traffic})")
# HN also supports search
results = await hn.search("Python")
for item in results[:3]:
print(f" {item.keyword}")
asyncio.run(main())
Phase 1–3 Intelligence APIs
# Lifecycle prediction
from trend_pulse.core.intelligence.lifecycle import predict_lifecycle, LifecycleStage
stage = predict_lifecycle(current_score=75, history=[{"score": s} for s in [20, 35, 50, 65]])
# -> LifecycleStage.PEAK
# Trend clustering
from trend_pulse.core.intelligence.clusters import cluster_trends
clusters = await cluster_trends(items, threshold=0.25)
# 6-agent content workflow
from trend_pulse.core.agents.workflow import run_content_workflow
state = await run_content_workflow(
trends=items,
platforms=["threads", "x"],
brand_voice="casual",
topic="AI tools",
)
content = state["final_content"] # {"threads": "...", "x": "..."}
# Hybrid scoring (heuristic + optional Claude API)
from trend_pulse.core.scoring.hybrid import score_content
result = await score_content("Your post content", "threads")
print(result.total, result.grade, result.mode) # 78.5, B+, heuristic
# Vector similarity search
from trend_pulse.core.vector.simple import SimpleVectorStore
store = SimpleVectorStore()
await store.upsert(items)
similar = await store.search_similar("artificial intelligence", k=5)
MCP Server (for Claude Code / AI agents)
Step 1: Install uv (if you don't have it)
# macOS
brew install uv
# Linux / WSL
curl -LsSf https://astral.sh/uv/install.sh | sh
Step 2: Add to .mcp.json
Create or edit .mcp.json in your project root (or ~/.claude/.mcp.json for global):
{
"mcpServers": {
"trend-pulse": {
"command": "uvx",
"args": ["--from", "trend-pulse[mcp]", "trend-pulse-server"],
"type": "stdio"
}
}
}
That's it. No pip install, no venv, no Python version management. uvx downloads and caches the package automatically on first run.
Step 3 (optional): Enable browser rendering
The render_page tool uses Cloudflare Browser Rendering to fetch JS-heavy pages. If you want this feature, add your Cloudflare credentials:
{
"mcpServers": {
"trend-pulse": {
"command": "uvx",
"args": ["--from", "trend-pulse[mcp]", "trend-pulse-server"],
"type": "stdio",
"env": {
"CF_ACCOUNT_ID": "your-cloudflare-account-id",
"CF_API_TOKEN": "your-cloudflare-api-token"
}
}
}
}
Get these from Cloudflare Dashboard → Workers & Pages → Overview. Skip this step if you don't need it — all other 28 tools work without any credentials.
Alternative: pip install
If you prefer a traditional install instead of uvx:
pip install "trend-pulse[mcp]"
{
"mcpServers": {
"trend-pulse": {
"command": "trend-pulse-server",
"type": "stdio"
}
}
}
Alternative: streamable HTTP (Docker, remote agents)
trend-pulse-server speaks stdio by default. Pass --transport streamable-http to serve MCP over HTTP instead:
trend-pulse-server --transport streamable-http --host 127.0.0.1 --port 8080
# MCP endpoint: http://127.0.0.1:8080/mcp
{
"mcpServers": {
"trend-pulse": {
"type": "http",
"url": "http://127.0.0.1:8080/mcp"
}
}
}
The HTTP endpoint has no authentication. Keep it on localhost (the default
--host) or put it behind an authenticating reverse proxy.
Available tools (29)
Trend Data (5):
Intelligence (5):
Content Guides (5):
All content guide tools return structured guides — the LLM does all judgment and creative work.
Agentic Content (8):
Operations (5):
Browser (1, optional — requires Cloudflare credentials):
Dashboard & REST API
# Start Streamlit dashboard
streamlit run src/trend_pulse/dashboard/app.py
# Start FastAPI REST API
uvicorn trend_pulse.dashboard.api:app --port 8000
# Docker Compose (all services)
cp .env.example .env # optional credentials, all blank by default
docker compose up
Docker Compose services (images are based on python:3.14-slim):
Notifications
from trend_pulse.notifications import DiscordWebhook, NotificationPayload
notifier = DiscordWebhook(webhook_url="https://discord.com/api/webhooks/...")
await notifier.send(NotificationPayload(
title="Trending now",
message="Claude AI is rising",
data={"keyword": "Claude AI", "score": 95},
))
Channels: Discord, Telegram, generic webhook, and email (SMTP). Credentials come from the environment; see .env.example. LINE Notify was shut down on 2025-03-31, so the line channel no longer delivers.
CLI Reference
trend-pulse trending [--sources SRC] [--geo CODE] [--count N] [--save]
trend-pulse search QUERY [--sources SRC] [--geo CODE]
trend-pulse history KEYWORD [--days N] [--source SRC]
trend-pulse snapshot [--sources SRC] [--geo CODE] [--count N]
trend-pulse sources
--sources: Comma-separated source IDs.
Built-in:
google_trends, hackernews, mastodon, bluesky, wikipedia, github, pypi,
google_news, lobsters, devto, npm, reddit, coingecko, dockerhub, stackoverflow,
arxiv, producthunt, lemmy, dcard, ptt
Plugins:
weibo, youtube_trending, threads, x_trending, tiktok_trending, line_today,
mobile01, bahamut, ettoday, yahoo_tw, udn, coinmarketcap, dexscreener,
pinterest, linkedin_trending, indie_hackers, xiaohongshu
--geo: ISO country code (e.g., TW, US, JP, DE).
- Google Trends / Google News: filters by country
- Wikipedia: selects language edition
- GitHub: treated as language filter (e.g.,
python) - Other sources: ignored (global data)
--save: Save results to local SQLite DB (~/.trend-pulse/history.db) for velocity tracking.
Plugin System
Drop a file into src/trend_pulse/plugins/sources/ — no registration needed. The PluginRegistry auto-discovers all modules that export a register() function.
from trend_pulse.plugins.base import PluginSource
from trend_pulse.sources.base import TrendItem
class MyPluginSource(PluginSource):
name = "my_plugin"
description = "My custom plugin source"
category = "global" # global, tw, dev, crypto, social, professional
frequency = "daily"
async def fetch_trending(self, geo="", count=20) -> list[TrendItem]:
return [TrendItem(keyword="...", score=80.0, source=self.name)]
def register():
return MyPluginSource()
Plugin categories: global, tw, dev, crypto, social, professional
For the legacy TrendSource interface (built-in sources), see docs/custom-sources.md and examples/custom_rss_source.py.
Velocity & Direction
When history data is available, each trend item includes:
{
"keyword": "Claude AI",
"score": 92,
"direction": "rising",
"velocity": 15.3,
"previous_score": 45.0,
"source": "hackernews"
}
Velocity = (current_score - previous_score) / hours_elapsed
Lifecycle Prediction
Each trend is assigned one of four lifecycle stages based on score trajectory:
Access via the get_lifecycle_prediction MCP tool or the Python API:
from trend_pulse.core.intelligence.lifecycle import predict_lifecycle, LifecycleStage
stage = predict_lifecycle(current_score=75, history=[{"score": s} for s in [20, 35, 50, 65]])
# -> LifecycleStage.PEAK
History Database
Snapshots are stored in SQLite at ~/.trend-pulse/history.db (override with TREND_PULSE_DB env var).
# Save snapshots over time
trend-pulse trending --save --count 5
# ... wait some time ...
trend-pulse trending --save --count 5
# Query history
trend-pulse history "Claude" --days 7
trend-pulse history "React" --days 30 --source npm
Output Format
All commands return JSON with a unified structure:
{
"timestamp": "2026-03-12T05:04:12Z",
"geo": "TW",
"sources_ok": ["google_trends", "hackernews", "reddit"],
"sources_error": {"wikipedia": "429 rate limited"},
"merged": [
{
"keyword": "Temporal: fixing time in JavaScript",
"score": 100,
"source": "hackernews",
"url": "https://...",
"traffic": "579 points",
"category": "tech",
"direction": "rising",
"velocity": 12.5,
"previous_score": 60.0,
"metadata": {}
}
],
"sources": {"hackernews": []}
}
Each item has a normalized score (0–100) for cross-source comparison.
Rate Limits
Content Guide Tools
MCP tools provide structured guides for creating viral content optimized against Meta's 7 ranking patents. The LLM does all judgment and creative work — tools only provide frameworks and criteria.
Threads Algorithm Penalty Pre-Checks
Before scoring, content is checked against 4 officially penalized patterns from the Threads Creator Page. Any violation blocks publishing:
Patent-Based Scoring (5 Dimensions)
Grades: S (90+), A (80+), B (70+), C (55+), D (<55)
Quality gate: overall >= 70, conversation durability >= 55.
Hybrid Scoring
score_content_hybrid (MCP) and score_content (Python API) run heuristic scoring by default. If ANTHROPIC_API_KEY is set and pip install "trend-pulse[llm]" is installed, a Claude API judge layer (claude-haiku-5-5) supplements the heuristic score. The mode field in the result indicates which path ran (heuristic or hybrid).
Review Checklist (15 Items)
Verdict: pass = all critical checks pass, fail = any critical check fails.
Threads Creator Insights
Key signals from the official Threads Creator Page:
- Posting frequency: 2–5 times per week (higher = more views per post)
- Replies: Account for ~50% of Threads views — actively reply to comments
- Media: Text + media significantly outperforms text-only
- Humor: Officially documented as performing well on Threads
- Topic tags: Multi-word tags with emojis increase reach
- 15 content types: TEXT, IMAGE, VIDEO, CAROUSEL, POLL, GIF, LINK_ATTACHMENT, TEXT_ATTACHMENT, SPOILER_MEDIA, SPOILER_TEXT, GHOST_POST, QUOTE_POST, REPLY_CONTROL, TOPIC_TAG, ALT_TEXT
Workflow: MCP Guides LLM
1. get_content_brief() → LLM gets writing guide with hook examples & strategies
2. LLM creates content → Original text based on brief
3. get_scoring_guide() → LLM runs penalty pre-checks, then self-scores 5 dimensions
4. LLM revises → Iterate until score >= 70
5. get_review_checklist() → LLM checks 15 items (7 critical / 5 warning / 3 info)
6. get_platform_specs() → LLM adapts for each platform
— or —
run_content_workflow() → 6-agent factory runs steps 1–6 autonomously
Python (Content Guide Tools)
from trend_pulse.content.briefing import (
get_content_brief,
get_scoring_guide,
get_review_checklist,
get_reel_guide,
)
from trend_pulse.content.adapter import get_platform_specs
# Get writing brief — hook examples, patent strategies, scoring dimensions
brief = get_content_brief("AI tools", "debate", "threads", lang="en")
print(f"Topic: {brief['topic']}, Char limit: {brief['char_limit']}")
print(f"Hook examples: {len(brief['hook_examples'])}")
print(f"Patent strategies: {len(brief['patent_strategies'])}")
# Get scoring guide — penalty pre-checks + 5-dimension evaluation framework
guide = get_scoring_guide("en")
print(f"Penalty pre-checks: {len(guide['penalty_precheck']['penalties'])}")
for name, dim in guide["dimensions"].items():
print(f"{name}: weight={dim['weight']}, {dim['description']}")
print(f"Grades: {list(guide['grade_thresholds'].keys())}")
# Get review checklist — 15 structured quality checks (7 critical)
checklist = get_review_checklist("threads", "en")
for item in checklist["checklist"]:
print(f"[{item['severity']}] {item['check']}")
print(f"Quality gate: overall >= {checklist['quality_gate']['min_overall']}")
# Get platform specs — char limits, algo priority, best times
specs = get_platform_specs("", "en") # all platforms
for name, spec in specs.items():
print(f"{name}: {spec['max_chars']} chars, best at {spec['best_times']}")
# Get reel guide — scene structure for video scripts
guide = get_reel_guide("educational", 30, "en")
for scene in guide["scene_structure"]:
print(f"[{scene['type']}] {scene['duration_seconds']}s — {scene['purpose']}")
Content Types
opinion, story, debate, howto, list, question, news, meme
Platform Limits
Security
render_pageincludes an SSRF guard: scheme whitelist + private IP block. Requests tofile://,127.x,10.x,192.168.x, and similar ranges are rejected.- Hybrid scoring's LLM judge uses prompt injection isolation to prevent trend content from influencing scoring instructions.
- The streamable HTTP transport has no authentication. It binds to
127.0.0.1by default, and Docker Compose publishes it on localhost only. - API keys are read from the environment only; the YouTube key is sent in a request header, never in the URL.
Requirements
- Python 3.10+ (tested on 3.10 through 3.15)
httpx(HTTP client)aiosqlite(history storage)- Optional:
mcp[cli]2.x for the MCP server (pip install "trend-pulse[mcp]") - Optional:
streamlit,plotly,pandas,fastapi,uvicornfor the dashboard (pip install "trend-pulse[dashboard]") - Optional:
anthropic1.x for hybrid LLM scoring (pip install "trend-pulse[llm]") - Optional:
twikitfor X/Twitter trends (pip install "trend-pulse[social]"; it does not import on Python 3.15 yet, sox_trendingfalls back to the guest API there) - Optional credentials: see
.env.example
The google, browser, and zh extras are deprecated and install nothing extra: no source uses trendspyg, pytrends, playwright, or beautifulsoup4.
License
MIT
Credits
Built by Claude World — the Claude Code community for developers.