Skip to main content
Glama
kaiding-ucb

podcast-summarizer-mcp

by kaiding-ucb

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
GEMINI_API_KEYYesYour Gemini API key from https://aistudio.google.com
VIDEO_ANALYSIS_JOBS_PATHNoPath to store job data~/.podcast-summarizer-mcp/jobs.json
VIDEO_ANALYSIS_STATE_PATHNoPath to store video state~/.podcast-summarizer-mcp/video-state.json
VIDEO_ANALYSIS_PROMPT_PATHNoPath to custom prompt file (re-read on every analysis)
VIDEO_ANALYSIS_CHANNELS_PATHNoPath to store channel registry~/.podcast-summarizer-mcp/channels.json
VIDEO_ANALYSIS_BATCH_METADATA_PATHNoPath to store batch metadata~/.podcast-summarizer-mcp/batches.json

Capabilities

Features and capabilities supported by this server

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
discover_new_videosA

Discover new videos from YouTube channels since the last time this MCP saw them.

Two modes for selecting channels:

  1. Pass channel_ids explicitly (legacy behavior).

  2. Omit channel_ids and the MCP reads from its tracked-channel registry (managed by add_tracked_channel / remove_tracked_channel). Use tag to filter the registry — e.g. tag="macro" returns only channels tagged macro.

State is tracked per channel in a server-managed JSON file. On the FIRST call for a channel, the most recent video is returned (and state is seeded) so the caller has something to analyze without ingesting the entire backlog.

Filters applied:

  • Livestreams (duration == 0) are skipped

  • Videos shorter than min_duration_seconds are skipped (default 10 minutes)

  • Videos older than or equal to the last-seen video are skipped

Args: channel_ids: Optional list of YouTube channel IDs. If omitted, the registry is used. tag: Optional tag filter for registry-based mode (ignored if channel_ids given). max_per_channel: How many recent uploads to inspect per channel (default 5) min_duration_seconds: Minimum video length to include (default 600 = 10min)

Returns: DiscoverResult with new_videos (newest first), skipped, channels_processed, and first_run_channels (channels that had no prior state).

analyze_video_startA

Launch Gemini video analysis as a background job. Returns in <1s with a job_id.

This is the default for both single-video and multi-video requests. For multiple videos, fire this tool N times back-to-back (each call returns in <1s with its own job_id) — the analyses run concurrently in background threads, so wall-clock time is bounded by the slowest single video (~3-10 min), NOT N × per-video time. Then poll analyze_video_result(job_id) for each job_id.

Use analyze_videos_batch_start ONLY when the user explicitly says "no rush", "overnight", or for scheduled / cron digests where minutes- to-hours latency is acceptable in exchange for 50% cost savings.

Podcast-length videos take Gemini 3-15 min to analyze, which exceeds typical MCP host request timeouts (Claude Desktop, Claude Code, OpenClaw all cap individual tool calls at ~60s). This tool spawns a background thread and returns immediately; poll analyze_video_result(job_id) to get the finished analysis.

Does NOT update discovery state. Caller is responsible for calling discover_new_videos (which manages state) beforehand.

Args: video_url: Full YouTube URL (https://www.youtube.com/watch?v=...) max_retries: How many Gemini retries on empty/short output (default 3) prompt: Optional override for the analysis prompt. If None, the default ships with an investment-podcast persona — set $VIDEO_ANALYSIS_PROMPT_PATH to change the host-wide default, or pass prompt here for a one-off override (e.g. "summarize this technical talk in 5 bullets").

Returns: { job_id, video_url, status: "pending" }

analyze_video_resultA

Poll for an analyze_video_start result. Blocks up to wait_seconds for a state change.

Default wait_seconds=10 keeps the blocking window well under the typical 60s MCP request timeout enforced by hosts. Smaller blocking window = guaranteed return well under 60s at the cost of more poll round-trips (cheap, ~1KB per poll).

Recommended pattern: call with wait_seconds=10 each poll. Cap total polling at ~90 iterations (~15 min) — most podcasts finish within 3-5 min. If a poll returns an MCP-transport error (not a status: "error" payload), the underlying job is likely still alive on the server — re-poll with the SAME job_id rather than starting over.

Args: job_id: The id returned by analyze_video_start wait_seconds: How many seconds to block waiting for completion (default 10, clamped to [0, 55] to stay safely under the MCP 60s request timeout)

Returns: { job_id, status, video_url, created_at, started_at, finished_at, result?, error?, attempts }

  • status="pending" or "running": still processing — poll again.

  • status="done": result holds the AnalysisResult dict (analysis, timestamps_valid, video_duration, vaneck_excluded, attempts, error=null).

  • status="error": error holds the message; result may hold a partial AnalysisResult.

  • status="not_found": job_id unknown (job purged after 6h TTL or wrong id).

analyze_videos_batch_startA

Submit a batch of YouTube videos to Gemini Batch API for async analysis.

Use this ONLY when the user explicitly says "no rush", "overnight", "do it later", or for scheduled cron digests. Batch is 50% cheaper than analyze_video_start but the wall-clock SLA is up to 24 hours (typically 15-60 min). For interactive requests — even multi-video ones like "summarize today's new videos" — prefer analyze_video_start fired N times in parallel (~5-10 min wall-clock for any N).

Args: video_urls: List of full YouTube URLs (https://www.youtube.com/watch?v=...) prompt: Optional override for the analysis prompt. If None, the default ships with an investment-podcast persona — set $VIDEO_ANALYSIS_PROMPT_PATH to change the host-wide default, or pass prompt here for a per-batch override.

Returns: { batch_job_name, video_count, video_urls, status: "pending", skipped: [{video_url, reason}] }

analyze_videos_batch_resultA

Poll the Gemini Batch API once; if done, return per-video results.

Call repeatedly until status is SUCCEEDED / FAILED / CANCELLED / EXPIRED. Recommended cadence: every 30-60s.

Args: batch_job_name: Name returned by analyze_videos_batch_start

Returns: { status, batch_job_name, results? (on SUCCEEDED), error? (on terminal failure) }

  • results: { video_id: AnalysisResult }

get_video_infoA

Cheap metadata-only lookup for a single YouTube video (1 YouTube API unit).

Use this to inspect a video's title, duration, channel, and publish date without burning Gemini tokens.

Args: video_url: Full YouTube URL or just the video ID min_duration_seconds: Threshold for the excluded_from_analysis flag (default 600)

get_stateA

Read-only view of the discovery state file.

Returns the last-seen video_id, published_at, and analyzed_at for each channel that has been processed. Useful for answering "what's the latest video I've seen from channel X?" without re-hitting YouTube.

Args: channel_ids: If provided, return only these channels. Otherwise return all.

search_youtube_channelsA

Search YouTube for channels matching a free-text query.

Use this when the user names a channel ("Forward Guidance", "All-In podcast") or describes one ("a good macro investing channel") and you need to identify candidate channels before adding to the registry.

No YouTube API key required — uses yt-dlp scraping under the hood.

Args: query: Free-text search term, e.g. "Forward Guidance" or "macro investing". max_results: How many distinct channels to return, ranked by relevance (default 5).

Returns: { "candidates": [ { "channel_id", "name", "channel_url", "hit_count", "confidence_score" }, ... ] } Empty list if nothing matches. Subscriber count + recent videos are NOT populated here — call get_channel_metadata(channel_id) for that.

resolve_youtube_channelA

Resolve a YouTube handle (@name) or channel URL to channel metadata.

Use this when the user gives an exact handle or URL — skips the search step. Returns None / error for video URLs, free-text strings, or channels that can't be loaded.

Args: handle_or_url: e.g. "@ForwardGuidance" or "https://www.youtube.com/@ForwardGuidance" or "https://www.youtube.com/channel/UCxxxxx".

Returns: { channel_id, name, handle, description, subscriber_count, channel_url, recent_video_titles } on success. { "error": "..." } on failure.

get_channel_metadataA

Fetch full metadata for a known channel_id.

Use after search_youtube_channels to enrich a candidate with subscriber count, description, and recent video titles before presenting to the user.

Args: channel_id: YouTube channel ID (must start with "UC").

Returns: { channel_id, name, handle, description, subscriber_count, channel_url, recent_video_titles }.

add_tracked_channelA

Add a channel to the registry. You MUST call this tool to add a channel — claiming "I've added X" without invoking it leaves the user's registry empty and the user has no way to know until they next ask "what am I tracking" and discover the missing channels.

Idempotent: re-adding the same channel_id updates name/handle/tags but preserves the original added_at timestamp. Tags are arbitrary strings — use them to group channels (e.g. ["macro"], ["semis", "podcast"]).

Args: channel_id: YouTube channel ID, must start with "UC". name: Display name (the user-friendly label). handle: Optional "@handle" (cosmetic, helps users identify the channel). tags: Optional list of grouping strings (default empty).

Returns: { added: true, channel: {channel_id, name, handle, tags, added_at}, registry_total: , registry_now_contains: [name, name, ...], # alphabetical user_facing_message: "Added X. Registry now has N channels: ..." } Use user_facing_message verbatim when telling the user the result — it carries the freshly-verified state and prevents misreporting.

remove_tracked_channelA

Remove a channel from the registry. You MUST call this tool to remove — claiming "I've removed X" without invoking it leaves the channel still tracked and produces silently wrong state.

No-op (returns removed=false) if the channel_id wasn't tracked. Does NOT clear the per-channel last-seen state, so re-adding later won't re-ingest the backlog.

Args: channel_id: YouTube channel ID.

Returns: { removed: true|false, channel_id, registry_total: , registry_now_contains: [name, name, ...], user_facing_message: "Removed X. Registry now has N channels: ..." } Use user_facing_message verbatim when reporting back to the user.

list_tracked_channelsA

List all channels currently in the registry. Always call this tool when the user asks "what am I tracking" or similar — never answer from memory or prior conversation context, since the registry can be mutated by other clients between turns.

Args: tag: If provided, return only channels carrying this tag.

Returns: { channels: [{channel_id, name, handle, tags, added_at}, ...], count, tag, user_facing_message: "Tracking N channels: ..." or "No channels tracked." } Use user_facing_message verbatim when reporting back.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

Latest Blog Posts

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/kaiding-ucb/podcast-summarizer-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server