youtube-analytics-mcp

youtube-analytics-mcp

Enables an AI assistant to query the full YouTube Analytics, Data v3, and Reporting API surface for one or multiple channels you own, with unrestricted parameters, local OAuth handling, and large-result output to files.

Category
Visit Server

README

youtube-analytics-mcp

An MCP server that gives an AI assistant the whole YouTube Analytics, Data v3 and Reporting API surface for channels you own — including several channels at once.

Most YouTube MCP servers hardcode a handful of metric strings, so the first question outside their preset list is unanswerable without forking them. This one is built the other way round: youtube_analytics_query takes every parameter reports.query accepts, and youtube_data_call / youtube_reporting_call do the same for the other two APIs. The presets are conveniences on top, never the only route to something.

You bring your own Google Cloud OAuth client. Nothing is shipped with this package, no credentials pass through any third party, and everything runs locally over stdio.

Tools

Tool What it does
youtube_accounts List authorized channels, the default, and where config lives
youtube_authorize Start adding a channel; returns the consent URL at once
youtube_authorize_status How the in-flight consent flow ended
youtube_authorize_cancel Abandon an in-flight consent flow
youtube_set_default_account Pick which channel unqualified calls use
youtube_forget_account Drop a stored refresh token
youtube_refresh_tokens Exercise every grant and report its age
youtube_analytics_query Unrestricted reports.query
youtube_data_call Unrestricted Data API v3
youtube_reporting_call Unrestricted Reporting API
youtube_session_report One video or stream: summary + traffic-source split
youtube_concurrent_curve One ended stream's concurrent viewers, minute by minute
youtube_capabilities What these APIs can and cannot answer

Every data tool takes an optional account, so one conversation can compare two channels.

Large results go to a file, not through the model

youtube_analytics_query, youtube_data_call and youtube_reporting_call take outputPath (and optional format: csv or json, otherwise inferred from the extension). With it, the full result is written to disk and only a summary — row count, columns, byte size, first three rows — comes back. Without it, results over 100 rows are truncated with a pointer to the option, because a thousand-row report returned inline costs the caller its context window and is unreadable when it arrives.

For genuinely bulk work — every day of every video, months at a time — use the Reporting API through youtube_reporting_call: it produces downloadable daily CSV reports with dimension combinations reports.query will not return in a single call.

Setup

1. A Google Cloud OAuth client, once

  • Create or pick a project.

  • APIs & Services → Library: enable YouTube Analytics API, YouTube Data API v3 and YouTube Reporting API.

  • OAuth consent screen → Audience: set user type to External (Internal is only offered when a Workspace organisation is attached). On that same Audience page, under Test users, click + Add users and add the Google account of every channel owner — including your own.

    Miss this and consent fails with "… has not completed the Google verification process. The app is currently being tested and can only be accessed by developer-approved testers." Being the project owner does not make you a test user; you have to add yourself explicitly.

  • Set publishing status to In production. This matters more than it looks. Google:

    A Google Cloud Platform project with an OAuth consent screen configured for an external user type and a publishing status of "Testing" is issued a refresh token expiring in 7 days, unless the only OAuth scopes requested are a subset of name, email address, and user profile.

    Every YouTube scope is sensitive, so a Testing app makes you re-authorize every week.

    Be warned that publishing is not simply a switch for these scopes: the console is likely to require a demo video and put the app through YouTube API verification review before it will let you leave Testing. That is real work for a personal tool, and weekly re-consent is often the better trade. See The 7-day grant limit below for the alternatives.

  • Credentials → Create credentials → OAuth client ID → Desktop app. Not Web application: this server listens on a random free loopback port each run, and a Web client requires every redirect URI, port included, to be registered in advance.

  • Download the JSON.

2. Tell the server where the client is

Put it in the config file (see config.example.json):

// %APPDATA%\youtube-analytics-mcp\config.json          (Windows)
// ~/Library/Application Support/youtube-analytics-mcp/  (macOS)
// ~/.config/youtube-analytics-mcp/config.json           (Linux)
{
  "client": { "client_id": "...", "client_secret": "..." }
}

Run youtube-analytics-mcp --where to print that directory. Environment variables work too and take precedence — YTMCP_CLIENT_ID + YTMCP_CLIENT_SECRET, or YTMCP_CLIENT_FILE pointing at Google's download verbatim (the {"installed": …} wrapper is unwrapped for you). YTMCP_CONFIG_DIR relocates the whole directory.

3. Authorize each channel

bun run auth                      # or: youtube-analytics-mcp --authorize
bun run auth -- --alias second    # name it yourself

Your browser opens on the consent page automatically; the URL is printed too, for the cases where it cannot (SSH, containers, CI). Pick the Google account that owns the channel and approve. Repeat for each channel — choose a different account in the browser each time. Accounts are named after their @handle unless you pass --alias.

Set YTMCP_NO_BROWSER=1 to never launch a browser, or pass openBrowser: false to the youtube_authorize tool for a single call.

Refresh tokens are written to accounts.json in the same directory, separate from the config.json you hand-edit, so the file you might paste into a bug report is never the file holding tokens. Both are written 0600 where the platform honours it.

Your assistant can also drive this. youtube_authorize returns the consent URL immediately and keeps listening in the background; youtube_authorize_status reports how it ended. It does not block, because consent takes as long as a human takes and MCP clients give up on a tool call long before that. The URL is also written to pending-auth.txt in the config directory, since most clients discard a server's stderr and a URL nobody can read is no use.

4. Register with your MCP client

Claude Code:

claude mcp add youtube-analytics --scope user -- bunx youtube-analytics-mcp

Or by hand, in any client's mcpServers map:

{
  "mcpServers": {
    "youtube-analytics": { "command": "bunx", "args": ["youtube-analytics-mcp"] }
  }
}

Read-only by default

Updating a video, posting or moderating comments, and uploading thumbnails are not reversible on a live channel, so the write scope is not requested and non-GET calls are refused. To enable them set YTMCP_ALLOW_WRITE=1 and re-authorize — the flag alone does nothing, because the stored token does not carry the scope.

Concurrent viewers, and the query shape nobody guesses

averageConcurrentViewers and peakConcurrentViewers do work on ended streams, and they match Studio's own numbers exactly. They are widely believed not to exist because the API refuses them in every shape but one: the filter must pin a single video and dimensions must be livestreamPosition.

query result
metrics=peakConcurrentViewers alone 400 The query is not supported
+ filters=video==ID 500 internal error
+ filters=video==ID;liveOrOnDemand==LIVE 400 — the extra filter is rejected
+ filters=video==ID + dimensions=livestreamPosition one row per minute of the stream

No error names the missing dimension, and the 500 in particular reads as the metric being broken rather than the request being wrong. youtube_concurrent_curve assembles it for you and returns the peak, the mean, and the whole minute-by-minute curve.

What it genuinely cannot give you

youtube_capabilities returns the current list. Both were checked by asking for the metric and getting Unknown identifier back, which is how the API distinguishes a name it has never heard of from one it knows but cannot serve here:

  • Live chat message and reaction totals. Studio-only. liveChatMessages reads a chat in real time and cannot recover an ended one.
  • Impressions and impression click-through rate. Studio-only, in the Reach tab.

Two things worth knowing

There is no "since published" window. The Analytics API is purely date-range, so a window covering a stream day returns that stream's live audience by construction. Studio's default per-video window excludes the entire live period, which is an easy and expensive trap when analysing live streams. This API cannot fall into it.

Analytics quota is separate. The Analytics and Reporting APIs meter independently of the Data API v3 daily unit budget, so querying here does not consume the quota that live chat polling competes for. Strong inference from them being distinct APIs with their own console quota pages — not measured.

Development

bun install
bun run dev          # start on stdio
bunx tsc --noEmit    # typecheck
bun run inspector    # MCP Inspector

MIT.

The API lags a few days

Finalized Analytics data is not available immediately. Measured on 2026-08-25, day-dimension rows ran through 08-22 and stopped: sessions from the previous three days returned no rows at all, not zero rows. A query for a stream that ended hours ago will look like a channel with no traffic.

Studio's web UI has a realtime path that the API does not expose, so same-day reporting still has to come from Studio. Use this server for everything older than roughly three days, where it is far better than clicking through Studio one video at a time.

The 7-day grant limit, and why no code can work around it

While the Cloud project's publishing status is Testing with an External user type, Google revokes refresh tokens after 7 days unless the only scopes requested are name, email and profile. Every YouTube scope is sensitive, so the exception never applies here.

This cannot be automated away. The 7 days is on the refresh token. Minting a new one requires a human approving a consent screen in a browser — that is what consent means, not a gap to engineer around. Refreshing access tokens more often does not touch it.

What this server does instead:

  • youtube_accounts reports each grant's ageDays and warns from day 5.
  • An expired grant fails with a message naming the cause and the fix, not a bare invalid_grant.
  • youtube_refresh_tokens (or --refresh from the CLI) exercises every grant as a health check. It is also a hedge: it is not established whether the 7-day clock is absolute from issuance or slides on use. If it slides, running this daily on a scheduler keeps grants alive indefinitely; if it does not, the call costs almost nothing. Worth running either way.
  • Re-consenting is one call to youtube_authorize, which opens the browser itself — about fifteen seconds.

The real fixes, in order of cost:

  1. Publishing status → In production. Free, and grants stop expiring. For sensitive YouTube scopes Google may require a demo video and verification review before it will let you publish, which is a real amount of work for a personal tool.
  2. Internal user type. No 7-day limit and no verification, but the option only exists when the project belongs to a Google Workspace organisation — a paid subscription.
  3. Live with weekly re-consent. For a single-user tool this is often the right answer.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured