youtube-studio-mcp
Provides tools for auditing and improving a YouTube channel's discoverability, including video catalog access, Analytics API metrics, search terms, retention curves, and underperformer ranking based on recoverable views.
Imports impressions and click-through rate data from YouTube Studio CSV exports and combines it with channel analytics to identify videos with the most recoverable views.
Click on "Install 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-studio-mcpWhich of my videos have the most recoverable views from low CTR?"
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 Studio MCP
An MCP server for auditing and improving a YouTube channel's discoverability.
It connects any MCP-capable AI agent to your own channel's data: catalog, Analytics API metrics, retention curves, inbound search terms, and the impressions and click-through rate that only a Studio CSV export exposes. It then ranks what is worth fixing by recoverable views rather than by click-through rate — see How underperformers are ranked.
Eight tools: auth_status, list_videos, get_video, query_analytics,
get_search_terms, get_retention_curve, import_studio_data, and
find_underperformers.
Everything is read-only and local: the SQLite cache, your OAuth tokens, and your Studio exports never leave your machine.
Requirements
Node.js ≥ 22
A Google account that owns the YouTube channel
Related MCP server: MCP YouTube Intelligence
Setup
1. Create a Google Cloud project and enable the APIs
Go to https://console.cloud.google.com/ and create a project.
Enable YouTube Data API v3 and YouTube Analytics API. (Both are in active use: the Data API backs catalog sync and
list_videos/get_video, and the Analytics API backsquery_analytics,get_search_terms, andget_retention_curve. Google Cloud Console only lets you add a consent-screen scope for an API you've enabled, so enable both before the next step.)
2. Configure the OAuth consent screen
Go to APIs & Services → OAuth consent screen.
Choose External and fill in the required fields.
Add these scopes:
https://www.googleapis.com/auth/yt-analytics.readonlyhttps://www.googleapis.com/auth/youtube.readonlyhttps://www.googleapis.com/auth/youtube.force-ssl
Important — publish the app to Production. While the app is in Testing, Google expires refresh tokens after 7 days, so you would have to re-authenticate every week. Click Publish app. The app stays unverified, which is fine: you are the only user and you are accessing your own data. You will see an "unverified app" warning once — choose Advanced → Go to (app name).
3. Create the OAuth client
APIs & Services → Credentials → Create credentials → OAuth client ID.
Application type: Desktop app.
Download the JSON.
4. Install and authenticate
npm install
npm run buildSave the downloaded OAuth client JSON as credentials.json in the server's
config directory (create the directory first if it doesn't exist):
# Linux/macOS — adjust the source filename to match what Google actually
# named your download (it starts with "client_secret_")
mkdir -p ~/.config/youtube-studio-mcp
mv ~/Downloads/client_secret_*.json ~/.config/youtube-studio-mcp/credentials.json# Windows (PowerShell) — same caveat about the source filename
New-Item -ItemType Directory -Force "$env:USERPROFILE\.config\youtube-studio-mcp" | Out-Null
Move-Item "$env:USERPROFILE\Downloads\client_secret_*.json" "$env:USERPROFILE\.config\youtube-studio-mcp\credentials.json"Then run:
node dist/index.js authThis opens your browser automatically. Authorize there, and the tokens are saved to
<config dir>/tokens.json. On Linux/macOS the file is written with owner-only
permissions (chmod 600); Windows has no equivalent file-permission bits, so
that step is a no-op there — rely on your user account's normal file
protections.
If no browser opens, the command also writes the authorization link to
<config dir>/authorize-url.txt — open that file and click the link. Do not
hand-copy the URL out of your terminal: it is ~520 characters, wraps across
several lines, and a truncated copy fails at Google with the misleading error
Required parameter is missing: response_type (the missing parameter is in the
part that got cut off, not in the request we build).
Set YTMCP_HOME to override the config directory (e.g. for a second channel
or a test setup). It replaces the whole ~/.config/youtube-studio-mcp
path, so credentials.json, tokens.json, and the SQLite cache all move
with it.
5. Register the server with your AI agent
The server speaks standard MCP over stdio, so any MCP-capable client can run it. You need
one thing in every case: the absolute path to dist/index.js in this repo.
Most clients share the same JSON shape. Substitute your own path:
{
"mcpServers": {
"youtube-studio": {
"command": "node",
"args": ["/absolute/path/to/youtube-studio-mcp/dist/index.js"]
}
}
}Agent | Where that JSON goes |
Claude Code |
|
Claude Desktop |
|
Cursor |
|
Windsurf |
|
Cline | the extension's |
Continue |
|
Gemini CLI |
|
Zed |
|
Two clients use a different shape.
VS Code / GitHub Copilot — .vscode/mcp.json, keyed servers, not mcpServers:
{
"servers": {
"youtube-studio": {
"type": "stdio",
"command": "node",
"args": ["/absolute/path/to/youtube-studio-mcp/dist/index.js"]
}
}
}OpenAI Codex CLI — ~/.codex/config.toml, TOML rather than JSON:
[mcp_servers.youtube-studio]
command = "node"
args = ["/absolute/path/to/youtube-studio-mcp/dist/index.js"]Restart the agent after editing its config. Ask it to run auth_status: it should name
your channel and report remaining quota. If it reports Authenticated: NO, re-run
node dist/index.js auth.
If your client is not listed, look for "MCP" in its settings — the command and args above are all any of them need. Config paths do move between releases, so check your client's own docs if a path here does not exist.
A note on agent behaviour
Every tool is annotated readOnlyHint: true, so agents that surface that hint will not
prompt for write confirmation. Nothing in this server modifies your channel — writing
metadata back is a later stage.
list_videos serves from the local cache unless asked to sync, and find_underperformers
and import_studio_data never touch the network at all. Only explicit syncs and Analytics
queries spend quota, which matters because agents explore: an agent that calls
list_videos twenty times costs nothing, while twenty catalog syncs would exhaust a day's
budget. auth_status reports what is left.
Tools
Tool | Purpose |
| Connection state, channel identity, remaining quota, local cache size |
| List and filter the catalog; |
| Full cached metadata and statistics for one video |
| Escape hatch onto the Analytics API — arbitrary metrics, dimensions, filters |
| The search queries that brought viewers in, cross-referenced against the video's metadata |
| Where viewers stop watching, as annotated drop-offs rather than raw points |
| Import impressions and CTR from a Studio CSV export — the one metric the Analytics API does not expose. Reads a local file; no authentication or quota needed |
| Rank the catalog by recoverable views — impressions times the gap to the channel's impression-weighted CTR baseline. Needs a Studio export imported first; reads local data only |
list_videos serves entirely from the local SQLite cache unless you pass
sync: true — plain reads (filtering by Shorts/long-form, view count,
publish date, title, or sorting) cost no quota. If nothing has been synced
yet, it tells you to call it again with sync: true instead of returning an
empty list.
get_search_terms and get_retention_curve cache their results per date
window (see Quota below); query_analytics does not cache and
always makes a live call. get_search_terms returns at most 25 rows —
Google caps the underlying report there, so a wider date range changes which
terms rank in the top 25 rather than how many rows come back.
Quota
YouTube grants 10,000 units/day plus a separate 100 search.list calls/day.
The server tracks both and holds back a reserve (500 units, 10 search calls) so a
bulk operation cannot leave interactive tools unusable. Quota resets at midnight
Pacific, which is what auth_status reports.
A full catalog sync (list_videos with sync: true) makes one channels.list
call, then pages the uploads playlist (playlistItems.list, 50 videos per
page) and fetches video details in batches (videos.list, 50 IDs per call),
each call costing 1 unit. That works out to 1 + ceil(videos/50) + ceil(videos/50)
units — about 5 units for a 100-video channel.
The YouTube Analytics API has its own per-project quota in Cloud Console, separate from
the Data API's 10,000 units. Analytics calls are recorded in the local ledger at zero unit cost, so
auth_status will not show them draining your Data API budget.
Search-term results and retention curves are cached per date window, because the underlying
reports return a ranked top-N over a range rather than per-day rows. A repeat call with the same
dates serves from the cache; pass refresh: true to re-query.
Importing impressions and CTR
impressions and impressionClickThroughRate do not exist in the YouTube Analytics API —
they are Studio-only. To get them:
YouTube Studio → Analytics → Advanced mode (top right)
Make sure the Impressions and Impressions click-through rate columns are visible — the export contains only the columns currently on screen
Export → Comma-separated values (.csv) — you get a zip containing three files
Unzip it, then run
import_studio_datawith the folder path
import_studio_data only reads a file from disk — it never calls the YouTube Data API or the
Analytics API, so it needs no authentication and costs no API quota.
The date window is read from the folder name (Studio names it like
Contenido 2010-01-26_2026-08-09 Channel). To override it, pass both rangeStart and
rangeEnd (YYYY-MM-DD) — supplying only one is rejected with a validation error rather than
silently falling back to the folder-name window, since that could land data under the wrong
dates with no warning. Both dates must be real calendar dates (2026-13-45 is rejected, not
rolled over) and rangeStart must not be after rangeEnd.
Impressions and CTR are a whole-range aggregate. Of the three files in the export, only
the per-video table (Datos de la tabla.csv / Table data.csv) carries impressions and CTR,
and it reports one row per video summed over the entire date range — there is no daily CTR
anywhere in the export. The per-day file (Datos del gráfico.csv / Chart data.csv) and the
channel-totals file (Totales.csv / Totals.csv) carry views only. So a comparison across
time means importing several exports with different ranges, not slicing one.
import_studio_data accepts either the export folder or a specific CSV path. Point it at the
folder and it finds the table file automatically. Point it at one of the other two files
directly and the import is rejected outright: a CSV's header tells you unambiguously which of
the three reports it is (reportType is table, chart, or totals — see
src/studio/csvSchemas.ts), and only table carries anything this importer can store. The
chart file does have a video id, so a naive import would silently succeed while overwriting
impressions/CTR with NULL and views with the last day's figure instead of the range total;
the totals file has no video id at all. Both are rejected before anything is written, with a
message naming Datos de la tabla.csv / Table data.csv as the file to point at instead.
Rows for videos that are no longer public are stored and reported as unmatched; that is expected, not an error.
Shorts
A video counts as a Short only if it is 180 seconds or shorter and was published on or after 2020-09-14, the day Shorts launched.
Duration alone is not enough. On a catalog of naturally short long-form video — music videos,
edits, trailers — a duration-only rule misclassifies wholesale. Live verification against a
dormant pre-2020 channel flagged roughly 70% of its catalog as Shorts, every one a false
positive: the channel's newest upload predated the Shorts launch by months, so not one of
them could have been genuine.
find_underperformers reads this flag through its cohort parameter. Passing cohort: 'short' or cohort: 'long' restricts the baseline to that half of the catalog, so Shorts
and long-form are compared only against their own kind. The default, cohort: 'all', does
not do that segmenting — it pools both into a single blended baseline. On a catalog that is
one cohort already (this user's real case, entirely long-form) pooling is a no-op, but on a
mixed catalog the default blends two populations with different typical CTRs; pass cohort
explicitly to segment them. A wrong flag on a video would produce confident nonsense rather
than an obvious error, which is why the publish-date guard matters.
How underperformers are ranked
find_underperformers ranks by recoverable views, not by click-through rate:
recoverable views = impressions x (baseline CTR - video CTR) / 100That is an estimate of the views a video would have gained at the channel's own baseline — the quantity worth acting on. Ranking on CTR alone is misleading, in three specific ways:
Impressions concentrate. Most of a channel's impressions sit in a small fraction of its videos, so "worst CTR" and "biggest opportunity" are close to disjoint sets. The video with the ugliest ratio is often one almost nobody was shown.
Zero impressions produce a 0% CTR by division, not by performance. Sorting ascending puts every never-surfaced video at the top of the list of things to fix, which is exactly backwards.
The highest CTR on a channel is usually a tiny denominator — a handful of impressions that happened to convert. It is noise presented as a triumph.
So two rules follow. Videos below an impressions floor are reported as insufficient data and never ranked as poor performers. And the baseline is impression-weighted, because an unweighted mean is dominated by low-traffic videos and describes almost none of the traffic the channel actually gets.
Each opportunity is typed. weak_metadata means the metadata score is low enough that it is
the thing to fix first; low_ctr means the metadata is already sound and the thumbnail or
title framing is the lever.
Development
npm test # unit tests, no network
npm run typecheck
npm run buildnpm run typecheck runs two projects: tsconfig.json (src/**, the build)
and tsconfig.test.json (src/** + test/** + vitest.config.ts, noEmit
only). Run just the test project with npm run typecheck:test. Vitest itself
only strips types via esbuild and does not type-check, so npm run typecheck
is what actually catches a type error in a test file.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceAn MCP server that enables interaction with the YouTube Data API, allowing users to search videos, get video and channel details, analyze trends, and fetch video transcripts.
- AlicenseAqualityCmaintenanceAn MCP server for intelligent YouTube video analysis that provides token-optimized summaries, sentiment analysis, and entity extraction from transcripts. It enables AI assistants to perform video reporting, channel monitoring, and comprehensive YouTube searches through structured data tools.1050Apache 2.0
- AlicenseAqualityFmaintenanceA comprehensive MCP server integrating YouTube Data, Analytics, and Reporting APIs, providing 40 tools for channel management, analytics, video publishing, transcripts, SEO, and comments.4017MIT
- FlicenseAqualityCmaintenanceMCP server for YouTube channel deep analytics, extracting transcripts and computing quantitative metrics like WPM, profanity, and humor taxonomy, with multi-creator comparison dashboards.51
Related MCP Connectors
Conformance checker for MCP servers. Free, no key, verdicts recomputable and re-measured daily.
An MCP server for deep research or task groups
SEO MCP server: crawl your site, find AI-visibility gaps, and ship the fix from your coding agent.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/jaimebg/youtube-studio-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server