Open-source competitive intelligence dashboard powered by the Tabstack API.
Rival tracks competitor pricing, changelogs, careers, docs, social, GitHub, and profile signals, stores historical scans, highlights changes, and surfaces extraction quality telemetry so schemas can improve over time.
- Scheduled and manual competitor scans
- Deep Dive research mode (
fast/balanced) with citations - API telemetry and schema quality analytics at
/insights - Demo mode (
/demo) with anonymous rate-limited scans - Diff summaries and competitor intelligence briefs
- MCP server — query all competitor data from Claude Desktop, Claude Code, or any MCP-compatible AI client
- Next.js (App Router) + TypeScript
- PostgreSQL + Prisma
- Tabstack SDK (
@tabstack/sdk) - Optional notifications via email + Slack webhook
- Install dependencies:
npm install- Configure environment:
cp .env.example .envRequired env vars:
DATABASE_URLTABSTACK_API_KEY
Recommended env vars:
CRON_SECRETINTERNAL_API_KEY(required for protected internal API routes like/api/scanand/api/brief)RESEND_API_KEY(if email notifications enabled)SLACK_WEBHOOK_URL(if notifications enabled)
- Generate Prisma client and apply migrations:
npx prisma generate
npx prisma migrate deploy- Seed competitor/page config:
npm run db:seeddb:seed is idempotent for competitors (upserts by slug) and resets/recreates configured pages for each competitor.
- Start app:
npm run devRival reads competitor definitions from rivals.config.json.
Supported type values for pages:
pricingcareerschangelogdocsgithubsocialprofilestackcustom
Minimal example:
{
"competitors": [
{
"name": "Acme",
"slug": "acme",
"url": "https://acme.com",
"manual": {
"manual_last_updated": "2026-04-01"
},
"pages": [
{ "label": "Pricing", "url": "https://acme.com/pricing", "type": "pricing", "geo_target": "US" },
{ "label": "Changelog", "url": "https://acme.com/changelog", "type": "changelog" }
]
}
]
}Rival uses all major Tabstack endpoint families.
Why: structured field extraction for schema-bound page types.
Used for:
pricingcareersdocsgithubsocialprofilestack
Key params:
urljson_schemaeffort(low/high)nocachegeo_target
Why: lower-cost text extraction for unstructured pages.
Used for:
changelogpages
Key params:
urleffortnocachegeo_target
Why: browser-agent fallback for JS-heavy or low-quality extraction cases.
Used for:
- fallback when
pricing/careersextraction is empty or fails custompages- demo scans with unknown page types
Key params:
urltaskguardrailsgeo_target
Why: transformation + synthesis of gathered intelligence.
Used for:
- page diff summaries
- competitor intelligence briefs
Key params:
urlinstructionsjson_schemaeffortnocache
Why: multi-pass web research for Deep Dive reports.
Used for:
/[slug]/deep-dive
Key params:
querymode(fast/balanced)nocache
Every Tabstack call is logged to api_logs with endpoint, params, status, fallback metadata, quality, missing fields, blocked/not-found flags, and duration.
Use /insights to close the loop:
- Identify most-missing fields by page type
- Identify fallback-heavy pages/endpoints
- Inspect top errors and blocked domains
- Refine schemas + routing policy
- Re-scan and measure improvement
Unexpected behavior discovered during e2e validation should be tracked in:
docs/experience-logging-followups.md- Follow-up GitHub issues labeled
experience-logging
Run after seeding and scans:
npm run validate:scan-cycleThis checks:
- pages have scan history
- diffs are being produced when prior scans exist
api_logsare populated- intelligence brief artifacts are present
Rival ships a built-in MCP server that exposes all competitor intelligence as read-only tools. Once registered, any MCP-compatible AI client (Claude Desktop, Claude Code, or custom agents) can query your competitor data directly — no manual exports, no copy-pasting.
| Tool | Description |
|---|---|
list_competitors |
All tracked competitors with threat tier, health score, and last change timestamp |
get_competitor |
Full snapshot — tracked pages, manual data (funding, G2, traffic) |
get_competitor_data |
Current extracted data by page type — pricing tiers, job listings, tech stack from JDs, GitHub stats, blog topics, review themes |
get_intelligence_brief |
AI-generated brief — positioning/content/product opportunities, threat reasoning, watch list, 7 axis scores |
get_deep_dives |
Agentic research reports with citations |
list_recent_intel |
Intel feed — recent changes, filterable by time, competitor, and page type |
get_competitor_diff |
Before/after content for a specific change |
search_intel |
Full-text search across the intel feed |
Build the server:
cd mcp && npm install && npm run buildAdd to your claude_desktop_config.json:
{
"mcpServers": {
"rival": {
"command": "node",
"args": ["/absolute/path/to/rival/mcp/dist/index.js"],
"env": {
"DATABASE_URL": "postgresql://..."
}
}
}
}For shared or remote access, run the server in HTTP mode with bearer auth:
RIVAL_MCP_TRANSPORT=http \
RIVAL_MCP_TOKEN=your-secret-token \
DATABASE_URL=postgresql://... \
PORT=3100 \
node mcp/dist/index.jsOr use the built-in HTTP endpoint that ships with the Next.js app at POST /api/mcp. Set RIVAL_MCP_TOKEN in your environment and point any MCP client at https://your-rival-instance.com/api/mcp with Authorization: Bearer <token>.
See mcp/README.md for full setup details and tool reference.
Public demo endpoint:
POST /api/demo- rate limit: 3 scans/day/IP
- single concurrent scan/IP
- anonymous logs (
is_demo=true, no competitor ID)
UI:
/demo
For production deploys, ensure startup sequence includes:
npx prisma migrate deploy
npm run db:seedUse a persistent PostgreSQL database and set CRON_SECRET for /api/cron.
- Pick a GitHub issue and use one focused branch/PR per issue.
- Keep Tabstack calls behind endpoint wrappers and logger.
- Run checks before opening PR:
npm run lint
npm run test
npm run typecheck- Document DX findings for Tabstack integration in your issue/PR notes.
See CONTRIBUTING.md for additional workflow expectations.
MIT