Skip to main content
Glama

yt-sub-mcp

유튜브 자막을 읽는 MCP 서버.

YouTube 웹 UI의 "스크립트 표시" 패널이 쓰는 내부 엔드포인트(youtubei/v1/get_panel)를 사용합니다. 흔히 쓰이는 /api/timedtext 경로와 달리 레이트리밋에 걸리지 않고, 4시간짜리 영상도 요청 한 번에 전체 자막이 옵니다. 왜 이 경로를 쓰게 됐는지는 문제 해결 과정에 적어뒀습니다.

  • 인증 불필요 — 쿠키도, API 키 발급도, 로그인도 없음

  • 외부 바이너리 없음 — yt-dlp도 ffmpeg도 안 씁니다

  • 영상당 요청 2회, 응답은 SQLite에 영속 캐싱

설치

git clone https://github.com/<you>/yt-sub-mcp.git
cd yt-sub-mcp
uv sync

Claude Code에 등록:

claude mcp add yt-sub -- uv --directory /absolute/path/to/yt-sub-mcp run yt-sub-mcp

Claude Desktop이라면 claude_desktop_config.json에:

{
  "mcpServers": {
    "yt-sub": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/yt-sub-mcp", "run", "yt-sub-mcp"]
    }
  }
}

Related MCP server: tubescribe-mcp

도구

list_subtitle_tracks(video)

사용 가능한 자막 언어 목록. 수동 제작인지 자동 생성인지 구분해서 보여줍니다.

2 track(s) for jNQXAC9IVRw:
        de  German (manual, translatable)
        en  English (manual, translatable)

get_subtitles(video, lang, timestamps, start_time, end_time, max_chars, refresh)

자막 본문.

인자

기본값

설명

video

URL 또는 11자 ID

lang

"en"

우선순위 순 콤마 구분 ("ko,en")

timestamps

false

블록마다 시작 시각 표시

start_time / end_time

초 단위 구간

max_chars

20000

문자 예산

refresh

false

캐시 우회

Korean (ko), manual | length 20:03 | excerpt 4:56-5:44

[4:56] 아무튼 그 중에 3명의 동방박사가 등장하는 장면 아시죠? ...

search_subtitles(video, query, lang, context, max_hits)

특정 표현이 나오는 지점을 타임스탬프·딥링크와 함께 반환합니다. 전체를 읽는 것보다 훨씬 쌉니다.

5 match(es) for 'creativity' in iG9CE55wbtY, in 4 passage(s):

[0:48] ... the extraordinary evidence of human creativity in all of ...
  https://www.youtube.com/watch?v=iG9CE55wbtY&t=48s

videowatch?v=, youtu.be, shorts, embed, live, 그리고 맨 ID를 모두 받습니다.

토큰 예산

1시간짜리 영상 자막은 대략 15k 토큰입니다. 그래서:

  • 출력은 max_chars에서 잘리고 이어볼 타임스탬프를 함께 반환합니다. 그 값을 start_time으로 넣으면 이어집니다.

  • 평문은 ~90단어 문단, 타임스탬프 모드는 ~30초 구간으로 병합해 렌더링합니다. 큐마다 타임스탬프를 붙이면 토큰이 3배가 됩니다.

  • timestamps는 기본 꺼짐. 인용이나 링크가 필요할 때만 켜세요.

  • 권장 흐름: search_subtitles로 위치를 찾고 → 그 구간만 get_subtitles로 읽기.

캐시

메모리 LRU(32) 앞단 + SQLite 영속 계층(500) 2단 구조입니다.

실측: 19분 영상 첫 요청 1,301ms → 이후 프로세스에서 0.7ms.

자막은 [text, start, duration] 삼중항 JSON을 zlib 압축해 저장합니다(427큐 25KB → 11KB, 43%).

환경변수

기본값

용도

YT_SUB_MCP_CACHE

1

0이면 디스크 계층 끔

YT_SUB_MCP_CACHE_DIR

아래

캐시 위치

YT_SUB_MCP_CACHE_TTL_DAYS

30

만료. 0이면 무기한

기본 경로는 macOS ~/Library/Caches/yt-sub-mcp/, 그 외 $XDG_CACHE_HOME/yt-sub-mcp/.

캐시는 기능을 죽이지 않습니다. 디스크가 차거나 DB를 열 수 없으면 로그만 남기고 메모리 전용으로 degrade합니다.

문제 해결 과정

이 프로젝트의 설계는 대부분 실패에서 나왔습니다. 기록해둡니다.

1. 처음엔 youtube-transcript-api로 만들었다

가장 흔한 선택입니다. 잘 돌아갔습니다 — 개발 도중 IP가 차단되기 전까지는.

HTTP 429 Too Many Requests  on /api/timedtext

약 25분간 도구 호출 20여 회 만에 걸렸습니다. 라이브러리는 이걸 IpBlocked라 부르지만 실제로는 단순 429, 즉 볼륨 기반 쿨다운입니다.

2. 차단의 범위를 좁혔다

호출당 HTTP 요청을 계측해보니:

list_subtitle_tracks  = 2 요청  (watch 페이지 → INNERTUBE player)
get_subtitles (cold)  = 3 요청  (위 2개 + /api/timedtext)

그리고 차단 중에도 watch 페이지와 InnerTube player는 정상 응답했습니다. 즉 429는 /api/timedtext 한 엔드포인트에만 걸립니다.

3. yt-dlp로 갈아타려다 접었다

유지보수가 활발하고 PO token·쿠키를 지원하니 답일 것 같았습니다. 그런데 yt-dlp도 player 응답에서 받은 timedtext URL을 그대로 씁니다. 같은 429를 맞았습니다.

클라이언트 로테이션도 시험했습니다. 12종 중 추출에 성공한 둘 모두:

android      추출 OK | timedtext 429
android_vr   추출 OK | timedtext 429

제한이 URL을 발급한 클라이언트가 아니라 IP에 걸려 있으니 당연한 결과였습니다. 마이그레이션은 취소했습니다.

4. get_transcript를 찾았지만 죽어 있었다

"InnerTube 스크립트 API는 레이트리밋이 느슨하다"는 자료가 여럿 있었습니다. protobuf params를 직접 만들어 YouTube가 watch 페이지에 심어둔 값과 대조했더니 바이트 단위로 일치했는데도 전부 400이었습니다.

player  (control)    HTTP 200
next    (control)    HTTP 200
get_transcript       HTTP 400
get_transcript+bad   HTTP 400   ← 쓰레기 params와 동일 응답

정상 params와 쓰레기 params의 응답이 같다는 건 본문 검증 이전에 거부된다는 뜻입니다. get_transcript는 폐기된 엔드포인트였고, 그걸 소개한 자료들이 낡은 것이었습니다.

5. 브라우저 캡처가 답을 줬다

실제 "스크립트 표시" 버튼을 누른 요청을 보니 엔드포인트가 교체돼 있었습니다:

POST /youtubei/v1/get_panel?prettyPrint=false
{ "panelId": "PAmodern_transcript_view", "params": "qgkPCgtNcXFXeFpMeldRTRgC" }

params를 디코딩하니 field 149 { 1: video_id, 3: 2 }, 이게 전부였습니다. 기존 get_transcript보다 훨씬 단순합니다.

6. 검증: timedtext가 429인 상태에서 전부 성공

/api/timedtext

/youtubei/v1/get_panel

레이트리밋

25~40요청 후 429

25요청 / 425분 분량 / 5초 — 무차단

인증

불필요

불필요 (logged_in=0 확인)

영상당 요청

3

2

4.4시간 영상

1요청, 2.6MB, 2053큐 완전

타임스탬프

ms + duration

초 단위, end 없음

큐 수 (20분 TED)

427

163 (읽기 좋게 병합됨)

위 측정은 모두 timedtext가 429로 막힌 상태에서 나왔습니다. 두 엔드포인트가 별도 예산이라는 뜻입니다.

남은 트레이드오프

타임스탬프가 초 단위이고 end 시각이 없습니다. 응답 어디에도 ms 필드가 없습니다. 각 큐는 다음 큐가 시작할 때까지, 마지막 큐는 영상 끝까지로 길이를 추론합니다. 표시가 M:SS이고 딥링크가 &t=Ns라 실질 손실은 작습니다.

없는 언어를 요청하면 조용히 기본 트랙으로 폴백합니다. 응답에 언어 표시가 전혀 없어서, hl을 던지기 전에 player 응답으로 트랙 목록을 먼저 확인하고 없으면 거부합니다. 영상당 요청이 2회인 이유가 이것입니다.

비공식 내부 엔드포인트입니다. 언제든 바뀔 수 있습니다 — get_transcript가 그렇게 죽었습니다. 다만 watch 페이지에서 params를 재추출하는 방법을 알고 있어 복구 경로가 있고, ProviderChain이 대체 소스를 끼울 자리를 남겨둡니다.

구조

providers/base.py       SubtitleProvider 프로토콜 + ProviderChain
providers/get_panel.py  유일한 provider — protobuf params, 파싱, 트랙 선택
errors.py               provider 중립 에러 + retryable 플래그
models.py               provider 중립 타입
formatting.py           병합 / 구간 슬라이싱 / 문자 예산
cache.py                메모리 LRU + SQLite + 합성 스토어
urls.py                 URL·ID 파싱
server.py               MCP 도구

models.py 타입과 errors.py 예외만 provider 경계를 넘습니다. 백엔드 고유 예외는 provider 안에서 번역됩니다.

provider가 하나뿐이라 ProviderChain은 지금 사실상 이음새입니다. 남겨둔 이유는 재시도 정책(SubtitleError.retryable)을 소유하고 있고, 두 번째 소스(timedtext 리더, 자막 없는 영상용 Whisper)를 도구 코드 변경 없이 끼울 자리이기 때문입니다.

폴백 정책

상황

에러

다음 provider 시도

429 / 연령 제한 / 로그인 요구

AccessBlocked

요청 언어 없음

LanguageNotAvailable

네트워크·파싱 오류

ProviderFailure

영상 삭제/비공개

VideoNotFound

자막이 꺼져 있음

SubtitlesDisabled

영구 실패에 다른 provider를 태워봐야 같은 답이므로 즉시 중단합니다.

개발

uv sync --dev
uv run pytest
uv run ruff check .
uv run ruff format .

서버가 실제로 뜨는지 확인:

uv run python scripts/smoke_stdio.py

테스트는 전부 오프라인입니다. conftest.py가 디스크 캐시를 끄고(개발자의 실제 캐시를 건드리지 않도록), provider 테스트는 캡처한 응답을 씁니다. CI에서 네트워크 호출이 생기면 테스트가 퇴행한 것입니다.

CI는 Linux/macOS × Python 3.12/3.13에서 lint·format·test를 돌리고, 별도 job이 stdio로 서버를 띄워 도구 목록을 확인합니다.

라이선스

MIT

Available Tools

3 tools
get_subtitlesA

Read a video's subtitles as text.

Returns human-written captions when they exist, otherwise the
auto-generated track. Output is truncated at `max_chars`; when that
happens the response ends with the timestamp to resume from, which
you pass back as `start_time` to read on.
ParametersJSON Schema
NameRequiredDescriptionDefault
langNoPreferred language codes, comma-separated and in priority order (e.g. 'ko,en'). Falls back left to right.en
videoYesYouTube URL or 11-character video ID
refreshNoBypass the cache and refetch. Transcripts are cached on disk for 30 days; use this only when the captions are known to have changed.
end_timeNoEnd of the excerpt, in seconds.
max_charsNoCharacter budget for the returned text.
start_timeNoStart of the excerpt, in seconds.
timestampsNoPrefix each block with its start time. Costs roughly 15% more tokens; enable only when you need to cite or link to moments in the video.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full responsibility for behavioral disclosure. It clearly explains truncation at max_chars, the resume mechanism via start_time, and the fallback from human-written captions to auto-generated tracks. These are meaningful behavioral insights beyond what annotations would provide. It does not mention caching behavior, but that is a minor omission given the schema covers the refresh param.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is exceptionally concise, with the core purpose in the first sentence and key behavioral details in a short second paragraph. Every sentence earns its place: the truncation/resume mechanism is explained in a clear, actionable way without fluff. It is front-loaded and easy to scan.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 7 parameters and a rich schema, the description covers the core behavior (reading text, fallback, truncation) but does not explain caching/refresh or language fallback in the description itself. However, these are fully documented in the schema, and an output schema exists, so the description need not detail return structure. It is sufficiently complete for an agent to use correctly, though a brief note on caching would make it more self-contained.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description adds value by explaining the relationship between max_chars and start_time for paginated reading, and clarifies the return behavior when truncation occurs. This goes beyond the schema's bare parameter definitions. Other parameters like lang, timestamps, and end_time are not explained in the description but are well-documented in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a clear, specific action: 'Read a video's subtitles as text.' It also distinguishes the tool from siblings (list_subtitle_tracks, search_subtitles) by focusing on reading the actual subtitle content rather than listing or searching tracks. The inclusion of fallback behavior further clarifies exactly what is returned.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for reading subtitle text, but does not explicitly state when to prefer this tool over the sibling tools. There is no mention of 'use this when you need the full transcript' or 'use list_subtitle_tracks to see available tracks.' It provides clear context but lacks explicit alternatives or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_subtitle_tracksA

List the subtitle tracks available for a video.

Use this when unsure which languages exist, or whether captions are human-written or auto-generated.

ParametersJSON Schema
NameRequiredDescriptionDefault
videoYesYouTube URL or 11-character video ID

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It implies a read-only operation via 'List,' but does not disclose details about output format, permissions, or any side effects. Since the tool is simple and the output schema exists, this is adequate but not rich.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, directly state the purpose and usage context. No wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple listing tool with one parameter and an output schema, the description covers purpose and when to use it. It doesn't need to explain return values due to the output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter 'video' is fully described in the schema as 'YouTube URL or 11-character video ID.' The description does not add additional semantic meaning beyond the schema, which already has 100% coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'List the subtitle tracks available for a video,' using a specific verb and resource. It distinguishes itself from sibling tools (get_subtitles, search_subtitles) by focusing on listing available tracks rather than retrieving or searching.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit when-to-use guidance: 'Use this when unsure which languages exist, or whether captions are human-written or auto-generated.' This gives clear context, though it doesn't mention alternatives or when not to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_subtitlesA

Find where a phrase is said in a video.

Cheaper than reading the whole transcript: returns matching passages
with timestamps and deep links. Follow up with `get_subtitles` and a
time range to read a hit in full.
ParametersJSON Schema
NameRequiredDescriptionDefault
langNoPreferred language codes, comma-separated.en
queryYesText to find; case-insensitive substring match.
videoYesYouTube URL or 11-character video ID
contextNoSegments of surrounding context per hit.
max_hitsNoMaximum matches to return.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It adds useful context about the return format (matching passages with timestamps and deep links) and performance characteristics (cheaper than reading the whole transcript). However, it does not mention potential failure modes, language availability, or limits, leaving some behavioral aspects undisclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three sentences, front-loaded with the primary purpose, then the benefit, then a follow-up suggestion. Every sentence earns its place, with no filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool has an output schema (all return values defined elsewhere), 5 parameters well-described in the schema, and clear sibling tools. The description adds the missing contextual piece: when to use it and how to combine it with get_subtitles, making it complete for the agent to invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema covers 100% of parameters with rich descriptions, so the baseline is 3. The description does not add meaning beyond the schema, only referencing the workflow indirectly with 'time range' for follow-up, which doesn't clarify any parameter. Thus a 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool finds where a phrase is said in a video, using a specific verb ('Find') and resource ('where a phrase is said in a video'). It distinguishes itself from siblings by explicitly positioning it as a search step and mentioning follow-up with get_subtitles.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context for when to use the tool ('Cheaper than reading the whole transcript') and names a specific alternative/follow-up (get_subtitles with a time range). It lacks an explicit 'when not to use' statement, so it doesn't fully meet the 5-level, but it's well-guided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

TDQS

A4.3/5.0
Disambiguation5/5

Each tool has a clear, distinct role: list tracks, read subtitles, and search within them. There is no overlap or ambiguity between the three operations.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern (list_, get_, search_). The naming is uniform and predictable, with no mixed conventions.

Tool Count5/5

Three tools is well-scoped for a YouTube subtitles server, covering the essential operations without unnecessary bloat. Each tool serves a necessary function.

Completeness4/5

The core operations of listing, reading, and searching subtitles are all present. A minor gap is the lack of an explicit way to select a specific subtitle track by language or ID, as get_subtitles appears to auto-pick human-written captions.

Maintenance

ActivityMaintained
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

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/YaleDevUni/yt-sub-mcp'

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