youtube-intel-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@youtube-intel-mcpRank these video candidates by opportunity score and cluster titles"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
youtube-intel-mcp
An MCP server that packages the competitor-intelligence scoring engine from yt-competitor-swipe as tools any MCP client — Claude, another agent, a script — can call directly, with no server-side network access and no API key.
You fetch the candidate videos however you like (the YouTube Data API, yt-dlp, an export you already have); this server scores, tags, and clusters them. It never fetches anything itself.
Why package it this way
The source engine's scoring math (a blended Opportunity Score across six signals) and its topic taxonomy are the reusable part — the part worth handing to any agent working on any channel, not just the pipeline it was built inside. An MCP server is the natural packaging for that: the scoring logic becomes a tool call instead of a Python import, callable from inside a Claude conversation, another agent's workflow, or a one-off script, with a typed, documented interface instead of "go read score.py."
Related MCP server: mcptube-vision
Tools
Tool | What it does |
| Blended 0-100 Opportunity Score: views-per-hour percentile, outlier multiple vs a channel baseline, engagement-velocity z-scores, demand gap, title-cluster convergence. Returns the input candidates annotated and sorted. |
| Ranks a keyword list by how under-served it is (strong demand, thin or stale competitor supply) and attaches a |
| Rule-based topic tagging from a title-hint taxonomy you supply, with an optional fallback to labels from an earlier human/model judgment pass. Never guesses; leaves a candidate untagged rather than inventing a label. |
| Groups a list of titles into subtopic clusters by their rarest shared significant token — deterministic, no external model call. |
Full argument shapes and return values are in each tool's docstring in
server.py (also what an MCP client sees when it lists tools).
60-second walkthrough
Clone to a scored result, no MCP client, no API key, no network call:
git clone https://github.com/svx2027/youtube-intel-mcp.git && cd youtube-intel-mcp # ~5s
python3 -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt # ~30s on a warm pip cache
python3 -c "
from server import score_candidates
result = score_candidates([
{'title': 'Best kettlebell for beginners', 'vph': 12.0, 'channel_id': 'c1', 'relation': 'direct'},
{'title': 'Kettlebell workout for beginners', 'vph': 400.0, 'channel_id': 'c2', 'relation': 'direct'},
])
for c in result['candidates']:
print(round(c['opportunity_score'], 1), c['title'])
"Output (verified by actually running the three commands above against this exact commit, not hand-typed):
30.0 Kettlebell workout for beginners
12.5 Best kettlebell for beginnersSame two titles, same views-per-hour shape a real competitor scan would
hand you; the one with 33x the velocity comes back ranked first with a
higher score. That loop — titles and a velocity number in, a ranked
Opportunity Score out — is the whole server. Everything past this point is
either running it as a long-lived MCP server instead of a one-off Python
call (next section), or the full argument/config shape (config.example.yaml,
each tool's docstring in server.py).
Quickstart
git clone https://github.com/svx2027/youtube-intel-mcp.git
cd youtube-intel-mcp
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python3 server.pyThat starts the server on stdio, ready for a desktop MCP client (Claude Desktop, Claude Code) to connect — see "Connect it to a client" below for the exact config for each.
Connect it to a client
Both of these point a client at the venv's own interpreter rather than a
bare python3, so the server starts with its dependencies even if the
client's own process never sourced .venv/bin/activate. Swap in your own
absolute paths (pwd after the cd above gives you the repo path).
Claude Desktop — edit the config file for your OS and add an entry
under mcpServers, then restart Claude Desktop:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.jsonLinux:
~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"youtube-intel": {
"command": "/absolute/path/to/youtube-intel-mcp/.venv/bin/python3",
"args": ["/absolute/path/to/youtube-intel-mcp/server.py"]
}
}
}Claude Code — one CLI command, no restart needed (it's read at the start of the next session, and you'll be prompted to approve a new project-scoped server the first time):
claude mcp add --scope project youtube-intel -- \
/absolute/path/to/youtube-intel-mcp/.venv/bin/python3 \
/absolute/path/to/youtube-intel-mcp/server.pyThat writes a .mcp.json in the current directory; editing it directly is
equivalent:
{
"mcpServers": {
"youtube-intel": {
"type": "stdio",
"command": "/absolute/path/to/youtube-intel-mcp/.venv/bin/python3",
"args": ["/absolute/path/to/youtube-intel-mcp/server.py"]
}
}
}--scope project shares the server with anyone who clones the repo
.mcp.json lands in; use --scope user instead for a server available to
you across every project, or --scope local (the default) to keep it
private to this one project and out of version control.
Either client now lists score_candidates, compute_demand_gap,
tag_topics, and cluster_titles as tools it can call directly in
conversation — no code, no terminal, just asking it to score a list of
titles.
Streamable HTTP, for a remote or web client
python3 server.py --transport streamable-http --port 8000Rate-limited by default: at most 120 HTTP requests per 60 seconds per
client IP (--rate-limit-max-requests / --rate-limit-window-seconds to
change either, --disable-rate-limit to turn it off). Stdio has no flag
for this at all — it is one local subprocess talking to one local client
over pipes, with no client IP to throttle in the first place, so there is
nothing for a rate limiter to do there. An HTTP endpoint is different: it
is reachable over a socket, so an unbounded one is a real
resource-exhaustion vector the moment --host is anything other than
127.0.0.1. Going over the limit gets a plain 429 with a JSON body
({"error": "rate_limited", "retry_after_seconds": ...}) and a
Retry-After header — see src/ratelimit.py for the limiter itself (an
in-memory, per-process fixed-window counter; it resets on restart and
isn't shared across workers, which is disclosed there rather than silently
assumed away).
A client now connects over HTTP at http://127.0.0.1:8000/mcp instead of
spawning the process on stdio — the same four tools, same inputs and
outputs, different transport. demo/http_client_demo.py is a real MCP
client exercising this end to end (initialize → list tools → call
score_candidates) over an actual socket; demo/session_transcript.txt is
a captured run of it against exactly the command above, if you want to see
the exchange without starting anything yourself:
# terminal 1
python3 server.py --transport streamable-http --port 8000
# terminal 2
python3 demo/http_client_demo.pyNo hosted demo endpoint. The natural next step — a Vercel (or
equivalent) deployment of this ASGI app so you can point a client at a URL
with nothing running locally — isn't live. The build environment this repo
is maintained from has outbound network access to GitHub, PyPI and npm
only; every hosting provider tried (Vercel, Fly.io, Render, Railway,
Cloudflare Workers, ngrok, Hugging Face Spaces) is blocked at the network
policy layer, not by anything about the app itself. mcp.streamable_http_app()
returns a standard Starlette ASGI app (server.mcp.streamable_http_app()),
so deploying it anywhere that runs Python ASGI apps is the same exercise as
deploying any other FastAPI/Starlette service — nothing in this repo is
Vercel-specific or missing for that step.
To call a tool without any MCP client at all, every tool is also a plain, directly callable Python function — see the "60-second walkthrough" above for a runnable example and its real output.
config.example.yaml has the full weights/thresholds/taxonomy shape each
tool accepts — copy the section you need into your own call, or load the
whole file and pass it straight through.
Architecture
flowchart LR
CLIENT["MCP client\n(Claude, an agent, a script)"] -->|"tool call"| SERVER(("server.py\nFastMCP"))
SERVER --> SCORE["src/scoring.py\nz-scores, outlier,\nlocal clustering,\nOpportunity Score"]
SERVER --> DEMAND["src/demand.py\nkeyword supply/demand gap"]
SERVER --> TAX["src/taxonomy.py\nrule-based topic tags"]
SCORE -.->|"demand_gap_score"| DEMAND
CLIENT -.->|"you fetch this yourself\n(YouTube Data API, yt-dlp,\nan export you already have)"| CANDIDATES["candidate videos/posts"]
CANDIDATES --> SERVEREvery tool call is stateless: no cache, no ledger, no config file read from disk inside the server. Everything the source engine read from a file (baselines, the seasonal calendar, a multi-day history ledger) is instead a plain argument here, or a signal this scaffold doesn't expose yet (see below) — a deliberate trade for a server any client can call without first setting up that engine's full repo layout.
Every tool validates its own input first (src/validation.py) — list vs.
dict, string vs. number, a batch-size cap (5000 items) — before any of it
reaches scoring.py/demand.py/taxonomy.py, which are written to trust
their caller and default a missing field rather than defend against a
malformed one. A bad call gets a clean MCP tool error naming the exact
field and index, not a raw traceback from deep inside the scoring math, and
an oversized batch is rejected outright rather than silently burning CPU
and memory on an unbounded list.
Honest scope
This is a scaffold: the four tools above, not the full pipeline.
No fetching. No YouTube Data API call, no yt-dlp, no vidIQ, no Gemini call happens inside this server. You bring the candidate data.
Clustering is local-only. The source engine's optional Gemini-assisted clustering path isn't wired up here —
cluster_titlesalways uses the same deterministic token-anchor method the source engine falls back to when Gemini is unavailable, so behavior here matches its documented, network-independent path.No calendar-tailwind signal. The source engine's sixth signal (does a title match an active seasonal-demand phase) depends on "today's date" and a calendar file; there's no
apply_calendartool yet, so thecalendarweight inscore_candidatesalways contributes 0. It's counted in the weights (so the other five still sum against the same total a caller of the source engine would expect) rather than silently dropped from the config shape.No sleeper / new-format history signals. Both depend on a multi-day ledger this stateless server doesn't keep. Not exposed; not silently half-implemented.
No hosted deployment. Streamable HTTP transport works (see Quickstart), so a client can reach it over a network instead of spawning it on stdio — but there's no URL to point at yet; you still run the process yourself, just on
--transport streamable-httpinstead of stdio. See the Quickstart section for exactly why no hosted endpoint is live and what deploying one would take.The rate limiter is in-memory and per-process, not production-grade. It resets on restart, isn't shared across multiple worker processes, and trusts
scope["client"]as-is with noX-Forwarded-Forhandling — correct for running this server directly, not for running it behind a proxy without that proxy doing its own real-IP handling first. Good enough to stop a single client from flooding a scaffold server with no auth ahead of it; not a substitute for a real gateway's rate limiting if you deploy this at any real scale.
Tests
python3 -m unittest discover -s tests -v108 tests, no network calls: the scoring math (z-scores, outlier fallback
order, local clustering, the blended score under custom weights), the
demand-gap signal (including the "alignment alone never flags" rule), rule-
based tagging, input validation (every malformed-input case src/validation.py
rejects, plus the same cases routed through each real tool function and
through mcp's own call_tool dispatch to confirm a caller gets a clean
tool error, not a traceback), the rate limiter (the fixed-window counter's
allow/block/reset behavior, and the ASGI middleware's 429 response over a
hand-built scope/receive/send — no real socket needed for this), a set of
server smoke tests confirming the MCP server imports cleanly and registers
exactly the four tools above, plus the --transport CLI parsing and
settings wiring for Streamable HTTP (the HTTP transport itself, rate
limiter included, is exercised over a real socket in
demo/http_client_demo.py, not in this no-network suite — see Quickstart).
Related tools
yt-competitor-swipe: drawn from that repo's
src/score.py,src/keyword_demand.py, andsrc/taxonomy.py— it runs the full pipeline this engine was built for (fetch, score, report, dashboard) end to end against a real niche. This repo exists for the case where you already have the candidate data and just want the scoring and tagging logic as a callable tool, from any MCP client.yt-competitor-outlier-pipeline: a different, keyless take on competitor outlier detection for a single channel, if you need the fetch layer too.
Full index of all public repos: github.com/svx2027.
License
MIT, see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Score supplied buying-signal evidence and prepare review-only why-now briefs. Read-only, no auth.
Score, rank and prioritize an existing Italian B2B lead list into explained JSON decisions.
Score a YouTube channel's upload pattern against YouTube's inauthentic-content policy.
Score pages against 11,485 public taxonomy categories by embedding similarity. $0.05/call via MCP.
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceEnables MCP clients to score the retention quality of the first three seconds of shortform video hooks using audio/video telemetry and transcript input, returning deterministic JSON results for viral hook optimization.9-
- FlicenseNot gradedqualityAmaintenanceEnables MCP clients such as Claude and Copilot to ingest YouTube videos into a persistent, compounding wiki knowledge base by combining transcripts with scene-change vision analysis, then answer natural-language questions by searching the compiled pages and returning candidates and structure for the client's own model to reason over. No API key is required in MCP mode, and knowledge from each new video enriches existing entity, topic, and concept pages.-
- FlicenseNot gradedqualityAmaintenanceEnables MCP clients to ingest and analyze video footage, then receive license-safe BGM recommendations with hook windows, beat/cut guidance, attribution, and ffmpeg ducking or mix specs.-
- FlicenseNot gradedqualityAmaintenanceEnables agents to ingest video files or URLs, analyze the footage for mood, energy, speech, scenes, and BPM, and get back a shortlist of license-safe BGM tracks with the license printed on every row. Also returns hook in/out windows, beat-snapped cut suggestions, and ready-to-run ffmpeg ducking and mix specs for editors and MCP clients.-