The motivating case: a claude.ai session is handed a YouTube link and needs the transcript. YouTube rate-limits direct fetches, and Tapir already holds captions-first transcripts in a shared, video-keyed store. The session should be able to hand the link to Tapir and get the text back — no browser, no copy-paste.
The HTTP API (#24, #25) makes this possible. This issue makes it native, alongside the existing brain and gitea MCP servers.
MCP server exposing Tapir over the API, following the ADR-0012 intent-named-verb charter:
summarize_video(url) — submit and return the video id plus current status
read_transcript(video_id) — transcript text plus source provenance
read_summary(video_id) — summary, highlights, takeaways, AI provenance
list_videos(filter?) — the caller's recent videos with status
Notes:
Tool names are intent-named verbs, not CRUD. No create_video, no get_transcript.
Authenticates with an API token from #23, resolved through the secret store — never an inline literal.
summarize_video is safe to call repeatedly: idempotent per #24.
Tool descriptions must state the terminal states plainly, especially that no_transcript means stop, not retry. An agent that does not know this will poll forever.
Serve at tapir-mcp.d-ma.be, consistent with the existing homelab MCP endpoints.
Non-goals
Write operations beyond submission. No deleting videos, no editing summaries.
Multi-user / delegated tokens. One token, one user.
Open question for review
Whether summarize_video should optionally block until terminal (bounded wait) rather than always returning immediately. Blocking is far better ergonomics for a chat session — one call instead of a poll loop — but it holds a connection and interacts awkwardly with the rate gate. Suggest shipping async-only first and revisiting once there is real usage.
Acceptance
caveman: me return a stored transcript to the calling agent, not fetch it from YouTube again
Red-first tests per tool
A shared transcript already in the store is served without touching YouTube — explicit test
Token resolved via the secret store; no credential in config or logs
Verified end to end against the motivating case: hand it a YouTube URL from a chat session, get the transcript back
Handoff
Branch from main, one file per commit, squash-merge PR back to main, human review gate.
## Problem
The motivating case: a claude.ai session is handed a YouTube link and needs the transcript. YouTube rate-limits direct fetches, and Tapir already holds captions-first transcripts in a shared, video-keyed store. The session should be able to hand the link to Tapir and get the text back — no browser, no copy-paste.
The HTTP API (#24, #25) makes this possible. This issue makes it native, alongside the existing `brain` and `gitea` MCP servers.
Depends on #23, #24, #25.
## Scope
MCP server exposing Tapir over the API, following the ADR-0012 intent-named-verb charter:
- `summarize_video(url)` — submit and return the video id plus current status
- `read_transcript(video_id)` — transcript text plus source provenance
- `read_summary(video_id)` — summary, highlights, takeaways, AI provenance
- `list_videos(filter?)` — the caller's recent videos with status
Notes:
- Tool names are intent-named verbs, not CRUD. No `create_video`, no `get_transcript`.
- Authenticates with an API token from #23, resolved through the secret store — never an inline literal.
- `summarize_video` is safe to call repeatedly: idempotent per #24.
- Tool descriptions must state the terminal states plainly, especially that `no_transcript` means stop, not retry. An agent that does not know this will poll forever.
- Serve at `tapir-mcp.d-ma.be`, consistent with the existing homelab MCP endpoints.
## Non-goals
- Write operations beyond submission. No deleting videos, no editing summaries.
- Multi-user / delegated tokens. One token, one user.
## Open question for review
Whether `summarize_video` should optionally block until terminal (bounded wait) rather than always returning immediately. Blocking is far better ergonomics for a chat session — one call instead of a poll loop — but it holds a connection and interacts awkwardly with the rate gate. Suggest shipping async-only first and revisiting once there is real usage.
## Acceptance
- [ ] `caveman: me return a stored transcript to the calling agent, not fetch it from YouTube again`
- [ ] Red-first tests per tool
- [ ] A shared transcript already in the store is served without touching YouTube — explicit test
- [ ] Token resolved via the secret store; no credential in config or logs
- [ ] Verified end to end against the motivating case: hand it a YouTube URL from a chat session, get the transcript back
## Handoff
Branch from `main`, one file per commit, squash-merge PR back to `main`, human review gate.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
The motivating case: a claude.ai session is handed a YouTube link and needs the transcript. YouTube rate-limits direct fetches, and Tapir already holds captions-first transcripts in a shared, video-keyed store. The session should be able to hand the link to Tapir and get the text back — no browser, no copy-paste.
The HTTP API (#24, #25) makes this possible. This issue makes it native, alongside the existing
brainandgiteaMCP servers.Depends on #23, #24, #25.
Scope
MCP server exposing Tapir over the API, following the ADR-0012 intent-named-verb charter:
summarize_video(url)— submit and return the video id plus current statusread_transcript(video_id)— transcript text plus source provenanceread_summary(video_id)— summary, highlights, takeaways, AI provenancelist_videos(filter?)— the caller's recent videos with statusNotes:
create_video, noget_transcript.summarize_videois safe to call repeatedly: idempotent per #24.no_transcriptmeans stop, not retry. An agent that does not know this will poll forever.tapir-mcp.d-ma.be, consistent with the existing homelab MCP endpoints.Non-goals
Open question for review
Whether
summarize_videoshould optionally block until terminal (bounded wait) rather than always returning immediately. Blocking is far better ergonomics for a chat session — one call instead of a poll loop — but it holds a connection and interacts awkwardly with the rate gate. Suggest shipping async-only first and revisiting once there is real usage.Acceptance
caveman: me return a stored transcript to the calling agent, not fetch it from YouTube againHandoff
Branch from
main, one file per commit, squash-merge PR back tomain, human review gate.