youtube-context-mcp 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- youtube_context_mcp-0.2.0/.github/workflows/ci.yml +36 -0
- youtube_context_mcp-0.2.0/.github/workflows/publish.yml +23 -0
- youtube_context_mcp-0.2.0/.gitignore +10 -0
- youtube_context_mcp-0.2.0/.python-version +1 -0
- youtube_context_mcp-0.2.0/LICENSE +21 -0
- youtube_context_mcp-0.2.0/PKG-INFO +140 -0
- youtube_context_mcp-0.2.0/README.md +112 -0
- youtube_context_mcp-0.2.0/pyproject.toml +60 -0
- youtube_context_mcp-0.2.0/src/youtube_context_mcp/__init__.py +7 -0
- youtube_context_mcp-0.2.0/src/youtube_context_mcp/__main__.py +6 -0
- youtube_context_mcp-0.2.0/src/youtube_context_mcp/proxies.py +43 -0
- youtube_context_mcp-0.2.0/src/youtube_context_mcp/server.py +114 -0
- youtube_context_mcp-0.2.0/src/youtube_context_mcp/transcripts.py +249 -0
- youtube_context_mcp-0.2.0/tests/__init__.py +0 -0
- youtube_context_mcp-0.2.0/tests/conftest.py +61 -0
- youtube_context_mcp-0.2.0/tests/test_proxies.py +55 -0
- youtube_context_mcp-0.2.0/tests/test_server.py +68 -0
- youtube_context_mcp-0.2.0/tests/test_transcripts.py +195 -0
- youtube_context_mcp-0.2.0/tests/test_url_parsing.py +48 -0
- youtube_context_mcp-0.2.0/uv.lock +929 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ['3.12', '3.13', '3.14']
|
|
15
|
+
env:
|
|
16
|
+
UV_PYTHON: ${{ matrix.python-version }}
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v5
|
|
22
|
+
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: uv sync
|
|
25
|
+
|
|
26
|
+
- name: Lint
|
|
27
|
+
run: uv run ruff check .
|
|
28
|
+
|
|
29
|
+
- name: Check formatting
|
|
30
|
+
run: uv run ruff format --check .
|
|
31
|
+
|
|
32
|
+
- name: Run tests
|
|
33
|
+
run: uv run pytest
|
|
34
|
+
|
|
35
|
+
- name: Build package
|
|
36
|
+
run: uv build
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Install uv
|
|
17
|
+
uses: astral-sh/setup-uv@v5
|
|
18
|
+
|
|
19
|
+
- name: Build package
|
|
20
|
+
run: uv build
|
|
21
|
+
|
|
22
|
+
- name: Publish to PyPI
|
|
23
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Onur Cetinkol
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: youtube-context-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A small MCP server for fetching YouTube video transcripts, wrapping youtube-transcript-api
|
|
5
|
+
Project-URL: Homepage, https://github.com/realiti4/youtube-context-mcp
|
|
6
|
+
Project-URL: Repository, https://github.com/realiti4/youtube-context-mcp
|
|
7
|
+
Project-URL: Issues, https://github.com/realiti4/youtube-context-mcp/issues
|
|
8
|
+
Author-email: Onur Cetinkol <onurcetinkol@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: mcp,model-context-protocol,subtitles,transcript,youtube
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Multimedia :: Video
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Requires-Dist: mcp>=1.12
|
|
25
|
+
Requires-Dist: requests>=2.32
|
|
26
|
+
Requires-Dist: youtube-transcript-api<2,>=1.2.4
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# youtube-context-mcp
|
|
30
|
+
|
|
31
|
+
A small [MCP](https://modelcontextprotocol.io) server that lets agents read YouTube video
|
|
32
|
+
transcripts so you can ask questions about a video, summarize it, or pull quotes.
|
|
33
|
+
|
|
34
|
+
It's a thin wrapper around [`youtube-transcript-api`](https://github.com/jdepoix/youtube-transcript-api),
|
|
35
|
+
which does all the actual fetching. This project just exposes it as two MCP tools.
|
|
36
|
+
|
|
37
|
+
> It returns a video's **existing captions/subtitles** — it does **not** transcribe audio
|
|
38
|
+
> (no Whisper/ASR). Videos without captions have nothing to return.
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
Run it on demand with [uv](https://docs.astral.sh/uv/) (no install needed):
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uvx youtube-context-mcp
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Or install it:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install youtube-context-mcp
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Use it with an agent
|
|
55
|
+
|
|
56
|
+
Add it to your MCP client config:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"mcpServers": {
|
|
61
|
+
"youtube-context": {
|
|
62
|
+
"command": "uvx",
|
|
63
|
+
"args": ["youtube-context-mcp"]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Or, in Claude Code:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
claude mcp add youtube-context -- uvx youtube-context-mcp
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Running over HTTP
|
|
76
|
+
|
|
77
|
+
By default the server talks **stdio** (the client launches it). If your client runs on a
|
|
78
|
+
different host — for example LM Studio on Windows while this server runs in WSL2 — run it as a
|
|
79
|
+
long-lived HTTP server instead and point the client at a URL:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
youtube-context-mcp --transport http --host 0.0.0.0 --port 8000
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Then add it by URL:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"mcpServers": {
|
|
90
|
+
"youtube-context": { "url": "http://localhost:8000/mcp" }
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
(`--host 0.0.0.0` makes it reachable from the Windows side; WSL2 forwards `localhost`.)
|
|
96
|
+
|
|
97
|
+
## Tools
|
|
98
|
+
|
|
99
|
+
| Tool | What it does |
|
|
100
|
+
| --- | --- |
|
|
101
|
+
| `get_transcript(video, languages=["en"], include_timestamps=False, translate_to=None)` | Returns the transcript as text. `video` is a URL or 11-char ID. Set `include_timestamps` for `[mm:ss]` / `[h:mm:ss]` lines; `translate_to` for an ISO language code. |
|
|
102
|
+
| `list_transcripts(video)` | Lists available transcripts (language, code, manual vs auto-generated, translatable) plus the translation targets. Use it when `get_transcript` can't find your language. |
|
|
103
|
+
|
|
104
|
+
## Proxies (optional)
|
|
105
|
+
|
|
106
|
+
YouTube blocks most datacenter/cloud IPs, so on a server you may hit `RequestBlocked` /
|
|
107
|
+
`IpBlocked`. Locally this is rarely needed. To route requests through a proxy, set env vars:
|
|
108
|
+
|
|
109
|
+
| Env var | Purpose |
|
|
110
|
+
| --- | --- |
|
|
111
|
+
| `WEBSHARE_PROXY_USERNAME`, `WEBSHARE_PROXY_PASSWORD` | Use [Webshare](https://www.webshare.io/) rotating residential proxies. |
|
|
112
|
+
| `WEBSHARE_PROXY_LOCATIONS` | Optional CSV of country codes, e.g. `us,de`. |
|
|
113
|
+
| `YT_TRANSCRIPT_HTTP_PROXY`, `YT_TRANSCRIPT_HTTPS_PROXY` | Use a generic HTTP/HTTPS proxy instead. |
|
|
114
|
+
| `YT_TRANSCRIPT_TIMEOUT` | Per-request timeout in seconds (default `20`). |
|
|
115
|
+
|
|
116
|
+
With no env set, requests go out directly.
|
|
117
|
+
|
|
118
|
+
## Troubleshooting
|
|
119
|
+
|
|
120
|
+
- **`RequestBlocked` / `IpBlocked`** — YouTube blocked the IP. Set the proxy env vars above.
|
|
121
|
+
- **No transcript found** — call `list_transcripts` to see which languages exist for that video.
|
|
122
|
+
- **Transcripts disabled** — the uploader turned captions off; nothing can be fetched.
|
|
123
|
+
|
|
124
|
+
## Development
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
uv sync
|
|
128
|
+
uv run ruff check . && uv run ruff format --check .
|
|
129
|
+
uv run pytest
|
|
130
|
+
uv run mcp dev src/youtube_context_mcp/server.py --with-editable . # interactive inspector
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
MIT
|
|
136
|
+
|
|
137
|
+
## Credits
|
|
138
|
+
|
|
139
|
+
All transcript fetching is done by [`youtube-transcript-api`](https://github.com/jdepoix/youtube-transcript-api)
|
|
140
|
+
by Jonas Depoix. This project is just an MCP adapter on top of it.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# youtube-context-mcp
|
|
2
|
+
|
|
3
|
+
A small [MCP](https://modelcontextprotocol.io) server that lets agents read YouTube video
|
|
4
|
+
transcripts so you can ask questions about a video, summarize it, or pull quotes.
|
|
5
|
+
|
|
6
|
+
It's a thin wrapper around [`youtube-transcript-api`](https://github.com/jdepoix/youtube-transcript-api),
|
|
7
|
+
which does all the actual fetching. This project just exposes it as two MCP tools.
|
|
8
|
+
|
|
9
|
+
> It returns a video's **existing captions/subtitles** — it does **not** transcribe audio
|
|
10
|
+
> (no Whisper/ASR). Videos without captions have nothing to return.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
Run it on demand with [uv](https://docs.astral.sh/uv/) (no install needed):
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
uvx youtube-context-mcp
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or install it:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install youtube-context-mcp
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Use it with an agent
|
|
27
|
+
|
|
28
|
+
Add it to your MCP client config:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"mcpServers": {
|
|
33
|
+
"youtube-context": {
|
|
34
|
+
"command": "uvx",
|
|
35
|
+
"args": ["youtube-context-mcp"]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or, in Claude Code:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
claude mcp add youtube-context -- uvx youtube-context-mcp
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Running over HTTP
|
|
48
|
+
|
|
49
|
+
By default the server talks **stdio** (the client launches it). If your client runs on a
|
|
50
|
+
different host — for example LM Studio on Windows while this server runs in WSL2 — run it as a
|
|
51
|
+
long-lived HTTP server instead and point the client at a URL:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
youtube-context-mcp --transport http --host 0.0.0.0 --port 8000
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Then add it by URL:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"mcpServers": {
|
|
62
|
+
"youtube-context": { "url": "http://localhost:8000/mcp" }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
(`--host 0.0.0.0` makes it reachable from the Windows side; WSL2 forwards `localhost`.)
|
|
68
|
+
|
|
69
|
+
## Tools
|
|
70
|
+
|
|
71
|
+
| Tool | What it does |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `get_transcript(video, languages=["en"], include_timestamps=False, translate_to=None)` | Returns the transcript as text. `video` is a URL or 11-char ID. Set `include_timestamps` for `[mm:ss]` / `[h:mm:ss]` lines; `translate_to` for an ISO language code. |
|
|
74
|
+
| `list_transcripts(video)` | Lists available transcripts (language, code, manual vs auto-generated, translatable) plus the translation targets. Use it when `get_transcript` can't find your language. |
|
|
75
|
+
|
|
76
|
+
## Proxies (optional)
|
|
77
|
+
|
|
78
|
+
YouTube blocks most datacenter/cloud IPs, so on a server you may hit `RequestBlocked` /
|
|
79
|
+
`IpBlocked`. Locally this is rarely needed. To route requests through a proxy, set env vars:
|
|
80
|
+
|
|
81
|
+
| Env var | Purpose |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| `WEBSHARE_PROXY_USERNAME`, `WEBSHARE_PROXY_PASSWORD` | Use [Webshare](https://www.webshare.io/) rotating residential proxies. |
|
|
84
|
+
| `WEBSHARE_PROXY_LOCATIONS` | Optional CSV of country codes, e.g. `us,de`. |
|
|
85
|
+
| `YT_TRANSCRIPT_HTTP_PROXY`, `YT_TRANSCRIPT_HTTPS_PROXY` | Use a generic HTTP/HTTPS proxy instead. |
|
|
86
|
+
| `YT_TRANSCRIPT_TIMEOUT` | Per-request timeout in seconds (default `20`). |
|
|
87
|
+
|
|
88
|
+
With no env set, requests go out directly.
|
|
89
|
+
|
|
90
|
+
## Troubleshooting
|
|
91
|
+
|
|
92
|
+
- **`RequestBlocked` / `IpBlocked`** — YouTube blocked the IP. Set the proxy env vars above.
|
|
93
|
+
- **No transcript found** — call `list_transcripts` to see which languages exist for that video.
|
|
94
|
+
- **Transcripts disabled** — the uploader turned captions off; nothing can be fetched.
|
|
95
|
+
|
|
96
|
+
## Development
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
uv sync
|
|
100
|
+
uv run ruff check . && uv run ruff format --check .
|
|
101
|
+
uv run pytest
|
|
102
|
+
uv run mcp dev src/youtube_context_mcp/server.py --with-editable . # interactive inspector
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT
|
|
108
|
+
|
|
109
|
+
## Credits
|
|
110
|
+
|
|
111
|
+
All transcript fetching is done by [`youtube-transcript-api`](https://github.com/jdepoix/youtube-transcript-api)
|
|
112
|
+
by Jonas Depoix. This project is just an MCP adapter on top of it.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "youtube-context-mcp"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "A small MCP server for fetching YouTube video transcripts, wrapping youtube-transcript-api"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"mcp>=1.12",
|
|
9
|
+
"youtube-transcript-api>=1.2.4,<2",
|
|
10
|
+
"requests>=2.32",
|
|
11
|
+
]
|
|
12
|
+
authors = [{name = "Onur Cetinkol", email = "onurcetinkol@gmail.com"}]
|
|
13
|
+
license = {text = "MIT"}
|
|
14
|
+
keywords = ["youtube", "transcript", "mcp", "model-context-protocol", "subtitles"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Environment :: Console",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
25
|
+
"Topic :: Multimedia :: Video",
|
|
26
|
+
"Topic :: Utilities",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/realiti4/youtube-context-mcp"
|
|
31
|
+
Repository = "https://github.com/realiti4/youtube-context-mcp"
|
|
32
|
+
Issues = "https://github.com/realiti4/youtube-context-mcp/issues"
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
youtube-context-mcp = "youtube_context_mcp.server:main"
|
|
36
|
+
|
|
37
|
+
[build-system]
|
|
38
|
+
requires = ["hatchling"]
|
|
39
|
+
build-backend = "hatchling.build"
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.wheel]
|
|
42
|
+
packages = ["src/youtube_context_mcp"]
|
|
43
|
+
|
|
44
|
+
[dependency-groups]
|
|
45
|
+
dev = [
|
|
46
|
+
"pytest>=8.0",
|
|
47
|
+
"ruff>=0.6",
|
|
48
|
+
"mcp[cli]>=1.12",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[tool.pytest.ini_options]
|
|
52
|
+
testpaths = ["tests"]
|
|
53
|
+
pythonpath = ["src"]
|
|
54
|
+
|
|
55
|
+
[tool.ruff]
|
|
56
|
+
line-length = 100
|
|
57
|
+
target-version = "py312"
|
|
58
|
+
|
|
59
|
+
[tool.ruff.lint]
|
|
60
|
+
select = ["E", "F", "W", "I"]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Build a youtube-transcript-api proxy config from environment variables.
|
|
2
|
+
|
|
3
|
+
YouTube blocks most datacenter/cloud IPs, so on a server the underlying library can raise
|
|
4
|
+
``RequestBlocked`` / ``IpBlocked``. These env vars let the user opt into a proxy without any
|
|
5
|
+
code change. With nothing set, requests go out directly.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
from youtube_transcript_api.proxies import (
|
|
13
|
+
GenericProxyConfig,
|
|
14
|
+
ProxyConfig,
|
|
15
|
+
WebshareProxyConfig,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def build_proxy_config() -> ProxyConfig | None:
|
|
20
|
+
"""Return a ``ProxyConfig`` derived from env vars, or ``None`` for direct requests.
|
|
21
|
+
|
|
22
|
+
Precedence:
|
|
23
|
+
1. ``WEBSHARE_PROXY_USERNAME`` + ``WEBSHARE_PROXY_PASSWORD`` -> ``WebshareProxyConfig``
|
|
24
|
+
(optional ``WEBSHARE_PROXY_LOCATIONS``, a CSV of country codes such as ``us,de``).
|
|
25
|
+
2. ``YT_TRANSCRIPT_HTTP_PROXY`` / ``YT_TRANSCRIPT_HTTPS_PROXY`` -> ``GenericProxyConfig``.
|
|
26
|
+
"""
|
|
27
|
+
webshare_user = os.environ.get("WEBSHARE_PROXY_USERNAME")
|
|
28
|
+
webshare_pass = os.environ.get("WEBSHARE_PROXY_PASSWORD")
|
|
29
|
+
if webshare_user and webshare_pass:
|
|
30
|
+
locations = os.environ.get("WEBSHARE_PROXY_LOCATIONS", "")
|
|
31
|
+
filter_ip_locations = [c.strip() for c in locations.split(",") if c.strip()]
|
|
32
|
+
return WebshareProxyConfig(
|
|
33
|
+
proxy_username=webshare_user,
|
|
34
|
+
proxy_password=webshare_pass,
|
|
35
|
+
filter_ip_locations=filter_ip_locations or None,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
http_proxy = os.environ.get("YT_TRANSCRIPT_HTTP_PROXY")
|
|
39
|
+
https_proxy = os.environ.get("YT_TRANSCRIPT_HTTPS_PROXY")
|
|
40
|
+
if http_proxy or https_proxy:
|
|
41
|
+
return GenericProxyConfig(http_url=http_proxy, https_url=https_proxy)
|
|
42
|
+
|
|
43
|
+
return None
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""MCP server exposing YouTube transcript tools over stdio."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
from typing import TypedDict
|
|
7
|
+
|
|
8
|
+
from mcp.server.fastmcp import FastMCP
|
|
9
|
+
|
|
10
|
+
from youtube_context_mcp import transcripts
|
|
11
|
+
|
|
12
|
+
mcp = FastMCP("youtube-context")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TranscriptInfo(TypedDict):
|
|
16
|
+
language: str
|
|
17
|
+
language_code: str
|
|
18
|
+
is_generated: bool
|
|
19
|
+
is_translatable: bool
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TranslationLanguage(TypedDict):
|
|
23
|
+
language: str
|
|
24
|
+
language_code: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TranscriptListing(TypedDict):
|
|
28
|
+
transcripts: list[TranscriptInfo]
|
|
29
|
+
translation_languages: list[TranslationLanguage]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@mcp.tool(structured_output=False)
|
|
33
|
+
def get_transcript(
|
|
34
|
+
video: str,
|
|
35
|
+
languages: list[str] | None = None,
|
|
36
|
+
include_timestamps: bool = False,
|
|
37
|
+
translate_to: str | None = None,
|
|
38
|
+
) -> str:
|
|
39
|
+
"""Fetch a YouTube video's existing captions as text so you can answer questions about it.
|
|
40
|
+
|
|
41
|
+
Returns existing captions/subtitles only; it does not transcribe audio. Videos without
|
|
42
|
+
captions have nothing to return.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
video: A YouTube URL (watch, youtu.be, shorts, embed, live) or an 11-character video ID.
|
|
46
|
+
languages: Preferred language codes in priority order. Defaults to ["en"].
|
|
47
|
+
include_timestamps: If true, prefix each line with [mm:ss] (or [h:mm:ss] past an hour).
|
|
48
|
+
translate_to: Optional ISO language code to translate the transcript into.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
The transcript as plain text.
|
|
52
|
+
"""
|
|
53
|
+
return transcripts.get_transcript(
|
|
54
|
+
video,
|
|
55
|
+
tuple(languages) if languages else ("en",),
|
|
56
|
+
include_timestamps,
|
|
57
|
+
translate_to,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@mcp.tool()
|
|
62
|
+
def list_transcripts(video: str) -> TranscriptListing:
|
|
63
|
+
"""List the transcripts available for a YouTube video.
|
|
64
|
+
|
|
65
|
+
Use this when get_transcript can't find your requested language. It reports each available
|
|
66
|
+
transcript (language, code, whether it's auto-generated, whether it's translatable) plus
|
|
67
|
+
the set of languages you can pass to get_transcript's translate_to.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
video: A YouTube URL or an 11-character video ID.
|
|
71
|
+
"""
|
|
72
|
+
return transcripts.list_transcripts(video)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def main() -> None:
|
|
76
|
+
"""Console-script entry point.
|
|
77
|
+
|
|
78
|
+
Defaults to stdio, for clients that spawn the server themselves. Use ``--transport http``
|
|
79
|
+
to run a long-lived HTTP server instead, which is handy when the MCP client lives on a
|
|
80
|
+
different host than the server -- e.g. LM Studio on Windows connecting to this server
|
|
81
|
+
running in WSL2 at ``http://localhost:8000/mcp``.
|
|
82
|
+
"""
|
|
83
|
+
parser = argparse.ArgumentParser(prog="youtube-context-mcp", description=main.__doc__)
|
|
84
|
+
parser.add_argument(
|
|
85
|
+
"--transport",
|
|
86
|
+
choices=["stdio", "http", "sse"],
|
|
87
|
+
default="stdio",
|
|
88
|
+
help="Transport to serve on (default: stdio).",
|
|
89
|
+
)
|
|
90
|
+
parser.add_argument(
|
|
91
|
+
"--host",
|
|
92
|
+
default="127.0.0.1",
|
|
93
|
+
help="Host to bind for http/sse (default: 127.0.0.1; use 0.0.0.0 to reach it from "
|
|
94
|
+
"Windows when running in WSL2).",
|
|
95
|
+
)
|
|
96
|
+
parser.add_argument(
|
|
97
|
+
"--port",
|
|
98
|
+
type=int,
|
|
99
|
+
default=8000,
|
|
100
|
+
help="Port to bind for http/sse (default: 8000).",
|
|
101
|
+
)
|
|
102
|
+
args = parser.parse_args()
|
|
103
|
+
|
|
104
|
+
if args.transport == "stdio":
|
|
105
|
+
mcp.run()
|
|
106
|
+
return
|
|
107
|
+
|
|
108
|
+
mcp.settings.host = args.host
|
|
109
|
+
mcp.settings.port = args.port
|
|
110
|
+
mcp.run(transport="streamable-http" if args.transport == "http" else "sse")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if __name__ == "__main__":
|
|
114
|
+
main()
|