LOCAL PREVIEW View on GitHub

Trending & Discovery MCP — Real-Time Trends and New Releases

Purpose

Surfaces what's hot right now — trending titles, new releases, bestsellers, and editorial picks — using real-time event streams rather than batch-computed indexes. Latency requirement: a title must appear in trending results within 5 seconds of crossing a popularity threshold.


Exposed Tools

Tool Input Output Use Case
get_trending genre?, demographic?, timeframe TrendingList "What's everyone reading?"
get_new_releases week?, genre? NewReleaseList Weekly new arrivals
get_bestsellers category, period BestsellerList Top sellers by category
get_editorial_picks theme? EditorialList Staff / editorial curations
get_seasonal_spotlight season SpotlightList Seasonal anime-tie-in titles

Architecture: Real-Time Trend Detection

flowchart TD
    EV([User Events\nview · purchase · wishlist · share]) --> KDS[Kinesis Data Streams\nPartitioned by manga_id]
    KDS --> KDA[Kinesis Data Analytics\nTumbling window 60s\nCount events per manga_id]
    KDA --> SC[Score Calculator\nWeighted: purchase×5 · share×3 · view×1]
    SC --> TH{Crosses trend\nthreshold?}
    TH -->|Yes| DY[(DynamoDB\nTrendingNow table\nTTL = 4h)]
    TH -->|No| DC[Discard]
    DY --> EC[(ElastiCache\nTrending cache TTL=30s)]
    EC --> MCP([Trending MCP\nget_trending tool])

    style EV fill:#4A90D9,color:#fff
    style MCP fill:#27AE60,color:#fff
    style TH fill:#8E44AD,color:#fff

Trend Score Formula

def compute_trend_score(events: list[Event], window_seconds: int = 60) -> float:
    weights = {
        "purchase":  5.0,
        "share":     3.0,
        "wishlist":  2.0,
        "view":      1.0,
        "review":    2.5,
    }

    raw_score = sum(weights.get(e.event_type, 1.0) for e in events)

    # Velocity bonus: score grows faster if it's accelerating
    prev_window_score = get_prev_window_score(events[0].manga_id)
    velocity_multiplier = 1 + max(0, (raw_score - prev_window_score) / prev_window_score)

    # Decay: older events within window contribute less
    time_decayed = sum(
        weights.get(e.event_type, 1.0) * (1 - (now() - e.timestamp).seconds / window_seconds)
        for e in events
    )

    return time_decayed * velocity_multiplier

New Releases Pipeline

flowchart LR
    PUB([Publisher submits\nnew volume to catalog]) --> CAT[Catalog MCP\nIngestion API]
    CAT --> DY2[(DynamoDB\nNewReleases table)]
    CAT --> OS[(OpenSearch\nCatalog Index)]
    DY2 --> EB2[EventBridge Rule\npublish_date = today]
    EB2 --> LA[Lambda: Tag\nnew_release=true]
    LA --> EC2[(ElastiCache\nnew-releases TTL=3600s)]
    EC2 --> MCP2([Trending MCP\nget_new_releases tool])

    style PUB fill:#4A90D9,color:#fff
    style MCP2 fill:#27AE60,color:#fff

Bestseller Aggregation

Bestsellers are computed from order data (Order MCP's RDS Aurora), not from views. This prevents popularity manipulation via bots generating fake views.

pie title Bestseller Score Composition
    "Units sold (60%)" : 60
    "Revenue generated (25%)" : 25
    "Reorder rate (10%)" : 10
    "Return rate penalty (-5%)" : 5

Bestseller tables are pre-computed every 15 minutes via a Step Functions workflow and cached in ElastiCache. Chatbot queries read from cache — never from RDS directly.


Seasonal Spotlight: Anime Tie-In Detection

When an anime adaptation of a manga begins airing, search traffic spikes 10-50×. The Trending MCP has a dedicated seasonal detection layer:

flowchart TD
    AN([Anime Calendar API\npoll every 6h]) --> AC{New anime\nthis season?}
    AC -->|Yes| ML[Manga-Anime Linker\nmatch by title + studio + source]
    ML --> SP[(DynamoDB\nSeasonalSpotlight table)]
    SP --> SC2[Score boost × 2.0\non Trending score]
    SC2 --> EC3[(ElastiCache\nSpotlight cache)]
    AC -->|No| NOP[No action]

    style AN fill:#4A90D9,color:#fff
    style SC2 fill:#E67E22,color:#fff

RAG Component: Editorial Picks

While trending and bestsellers are signal-driven, editorial picks use a lightweight RAG over a curator-authored document library:

flowchart LR
    TC([get_editorial_picks\ntheme='summer beach']) --> EB3[Embed theme query]
    EB3 --> OS3[(OpenSearch\nEditorial Picks Index\n~5,000 curated docs)]
    OS3 --> RK[Rerank top-5]
    RK --> TR([Tool Result\ncurated picks with editorial notes])

    style TC fill:#4A90D9,color:#fff
    style TR fill:#27AE60,color:#fff

Editorial documents are written by the content team, manually reviewed, and published weekly. They carry tags (summer, romance, coming-of-age) that are both keyword-searchable (BM25) and embedded (dense).


Time-Based Trend Windows

Timeframe Data Source TTL Use Case
last_hour Kinesis Analytics rolling 30s cache Breaking trends
today DynamoDB TrendingNow 5min cache Daily trending
this_week Step Functions aggregation 1h cache Weekly charts
this_month Redshift aggregate 6h cache Monthly bestsellers
all_time Redshift aggregate 24h cache Hall of fame

Failure Modes

Failure Symptom Mitigation
Kinesis shard hot spot One manga ID overwhelms a shard Partition key = manga_id + random_suffix_mod_10
Trend score inflation (bot traffic) Chart suddenly dominated by one title IP rate limiter at API Gateway; bot score from WAF applied as penalty multiplier
Cache stampede on Monday morning All caches expire simultaneously after weekend Jitter: TTL = base + random(0, 30%)
Anime tie-in over-boost One anime dominates all categories Category-level diversity cap: max 2 anime-tie-in titles per top-10
Kinesis Data Analytics lag Trend appears with 3-5min delay SLA: trending window is "near real-time within 5s" — acceptable; alert if >30s

Interview Grill

Q: How do you prevent a coordinated purchase campaign from fake-trending a title? A: Three layers: (1) WAF anomaly detection flags IPs with >50 events/minute. (2) Bot score from CloudFront is applied as a multiplier: bot_score=0.9 means events count as 10% of normal weight. (3) For chart-topping titles (top-5), an editorial review flag is raised and a human confirms before the title stays in position for >2h.

Q: Why Kinesis Data Analytics instead of Lambda for aggregation? A: Lambda aggregation requires external state storage (DynamoDB) for window computation, adding latency and cost. KDA runs stateful tumbling windows natively in-memory, producing per-window aggregate output with sub-second latency. The tradeoff: KDA is more expensive per-hour but cheaper at scale than the equivalent Lambda + DynamoDB pattern.

Q: What's the latency from a purchase event to appearing in trending? A: Kinesis ingestion: ~100ms. KDA tumbling window: fires every 60s. DynamoDB write: ~5ms. ElastiCache write: ~2ms. Next cache read: within 30s. End-to-end: up to ~90 seconds worst case, not 5 seconds. The "5 seconds" SLA refers to the delay from the trend threshold being crossed to the DynamoDB write — the visibility lag to end users is up to 90s (next cache miss cycle).

Q: How do seasonal spotlights avoid spoiling ongoing anime? A: The Anime Calendar API returns an is_adaptation_complete flag. If the anime is still airing, the spotlight description is limited to pre-airing manga content only. Volumes beyond the current anime episode are tagged spoiler_risk: true and excluded from the spotlight summary.