Skip to content

Releases: samson-art/transcriptor-mcp

v0.7.1: Bump version to 0.7.1 and enhance MCP features

05 Apr 19:00

Choose a tag to compare

Added

  • MCP_TRUST_PROXY: Parsed in src/env.ts (parseMcpTrustProxyEnv) and passed to Fastify trustProxy in src/mcp-http.ts so request.ip reflects the client behind reverse proxies (X-Forwarded-For). Supports boolean-ish strings, hop counts, or proxy-addr-style strings (unset/empty defaults to true).
  • Prometheus mcp_http_requests_by_client_ip_total: Counter with route, method, client_ip (src/metrics.ts, recordMcpHttpRequestByClientIp); incremented on onResponse with stable route labels (routeLabelForMcpHttpMetrics). Optional disable via MCP_METRICS_HTTP_REQUESTS_BY_CLIENT_IP (isMcpMetricsHttpRequestsByClientIpEnabled) to avoid high cardinality.
  • Anonymous MCP quota by client (HTTP): When there is no X-Api-Key, resolveLimit / enforceMcpToolQuota accept optional anonymous material (hashed as anon:<material>); HTTP supplies normalized client IP via McpRequestContext.anonymousQuotaMaterial and createMcpServer({ getAnonymousQuotaMaterial }). Stdio MCP omits the resolver and keeps the legacy single global anonymous bucket (__mcp_quota_anonymous_v1__).
  • IP helpers for quota/metrics: normalizeIpStringForQuota, normalizeMcpClientIp in src/mcp-http.ts (trim, bracketed IPv6, zone id strip, lowercase).

Changed

  • MCP HTTP: GET /sse session setup runs inside runWithMcpRequestContext(buildMcpHttpRequestContext(...)) so SSE tool calls see the same API key and anonymous quota material as streamable /mcp and /message.

Tests

  • src/env.test.ts: parseMcpTrustProxyEnv, isMcpMetricsHttpRequestsByClientIpEnabled.
  • src/mcp-http.test.ts: IP normalization and route label helpers.
  • src/mcp-quota.test.ts: Distinct anonymous buckets, enforceMcpToolQuota with anonymous material vs stdio global bucket.
  • src/mcp-request-context.test.ts: anonymousQuotaMaterial in context.
  • src/metrics.test.ts: mcp_http_requests_by_client_ip_total export.
  • E2E src/e2e/api-smoke.ts: Asserts mcp_http_requests_by_client_ip_total on GET /metrics when MCP quota metrics are checked.

v0.7.0: Bump version to 0.7.0 and introduce MCP tool call quota features

05 Apr 17:47

Choose a tag to compare

Added

  • MCP tool call quota (optional): Per-client limits keyed by X-Api-Key on MCP HTTP (src/mcp-quota.ts, src/mcp-core.ts). Enable with MCP_QUOTA_ENABLED; defaults MCP_QUOTA_DEFAULT_MAX / MCP_QUOTA_DEFAULT_WINDOW; optional strict mode MCP_QUOTA_REJECT_UNREGISTERED; customizable messages via MCP_QUOTA_CONTACT_MESSAGE, MCP_QUOTA_MESSAGE_NO_KEY, MCP_QUOTA_MESSAGE_INVALID_KEY.
  • Client API key registry (hashed secrets only): JSON file or inline env — MCP_CLIENT_API_KEYS_FILE (preferred) or MCP_CLIENT_API_KEYS_JSON, plus MCP_CLIENT_API_KEY_PEPPER. Validation, prefix keys, and lookup in src/api-key-registry.ts; map-based registry builder for fast hash lookup; src/mcp-quota-registry.ts re-exports loader helpers.
  • HTTP request context for quota: src/mcp-request-context.ts (AsyncLocalStorage) so streamable /mcp, POST /sse, and /message expose the client key to createMcpServer({ getClientApiKey }) (src/mcp-http.ts).
  • Prometheus metrics (MCP): mcp_quota_checks_total, mcp_quota_exceeded_total, mcp_quota_tool_calls_blocked_total, mcp_quota_http_429_total, mcp_quota_check_duration_seconds in src/metrics.ts; resetMetricsRegistryForTests() for unit tests.
  • Quota counter store: Fixed-window buckets in src/mcp-quota-store.ts — in-memory (MemoryQuotaCounterStore) for single-process; RedisQuotaCounterStore (Lua INCR + PEXPIRE) for shared Redis when wired in.
  • Duration parsing: parseQuotaWindowMs() in src/env.ts for quota windows (e.g. 24h, 30m, 1 minute).
  • MCP session config / Smithery: Optional apiKey in MCP_SESSION_CONFIG_SCHEMA and .well-known/mcp-config — gateway maps form field to X-Api-Key (distinct from MCP_AUTH_TOKEN / Bearer).
  • Monitoring stack (repo): monitoring/prometheus.yml, Grafana provisioning (monitoring/grafana/provisioning/...) and MCP quota dashboard JSON monitoring/grafana/provisioning/dashboards/files/mcp-quota.json. docs/monitoring.md — new quota metrics, PromQL snippets, dashboard mount notes.
  • Docs and repo hygiene: CONTRIBUTING.md (dev setup, make prepare / make check); SECURITY.md (supported versions, private reporting via GitHub Security Advisories). docs/configuration.md and .env.example — full quota and registry variable list.
  • Tests: src/api-key-registry.test.ts, src/mcp-quota.test.ts, src/mcp-quota-store.test.ts, src/mcp-request-context.test.ts, src/metrics.test.ts; MCP quota scenarios in src/mcp-core.test.ts; schema assertions in src/mcp-http.test.ts. E2E src/e2e/api-smoke.ts — optional MCP_QUOTA_ENABLED / high default max, asserts quota-related series on GET /metrics (skip with SMOKE_SKIP_MCP_QUOTA_METRICS).

Changed

  • Dockerfile: Build stage node:20-alpine; runtime base node:20-bookworm-slim (Debian, apt-get for yt-dlp/ffmpeg stack); comment clarifying Alpine vs Debian for system packages.
  • npm test: Jest runs with --forceExit to avoid hanging on open handles in CI.

Security

  • .gitignore: Ignore secrets/ to reduce risk of committing local key material.

v0.6.9: Update version to 0.6.9 and enhance playlist subtitle handling

31 Mar 12:45

Choose a tag to compare

Added

  • get_playlist_transcripts hardening (downloadPlaylistSubtitles in src/youtube.ts): Returns a discriminated DownloadPlaylistSubtitlesOutcome (ok + results or failure) instead of null on error. On yt-dlp failure, still scans the temp directory and returns partial results when any subtitle files were written (aligned with single-video runYtDlpAndExtractSubtitles).
  • --ignore-errors for playlist subtitle runs so one bad entry does not abort the batch. Opt out with YT_DLP_PLAYLIST_IGNORE_ERRORS=0. Documented in docs/configuration.md and .env.example.
  • YT_DLP_VERBOSE_ON_ERROR: When set to 1, after a failed playlist run with no partial files, runs yt-dlp once more with -v and without --quiet/--no-progress and logs stderr for diagnostics. Documented in docs/configuration.md and .env.example.
  • collectExecFileErrorDetails() and ExecFileErrorDetails: Normalized fields from failed execFile / yt-dlp runs (message, exitCode, signal, cmd, stdout, stderr) for structured logs.
  • formatPlaylistDownloadFailureMessage(): Builds the MCP/API-facing error string (message, exit code, stderr tail, operational hints).
  • appendYtDlpEnvArgs options: Optional third argument AppendYtDlpEnvArgsOptions with quiet: false to omit --no-progress and --quiet (used for verbose replay).

Changed

  • Playlist failure logging: Logs exitCode, signal, and cmd when present, not only empty stdout/stderr under --quiet.
  • MCP get_playlist_transcripts: On full failure, throws an error whose message comes from formatPlaylistDownloadFailureMessage instead of the generic Failed to fetch playlist subtitles.

Tests

  • src/youtube.test.ts: Coverage for collectExecFileErrorDetails, formatPlaylistDownloadFailureMessage, --ignore-errors / YT_DLP_PLAYLIST_IGNORE_ERRORS=0, failure outcome shape, and appendYtDlpEnvArgs with quiet: false.

0.6.8

23 Mar 03:02

Choose a tag to compare

Added

  • Background Whisper jobs and late cache write: When the client hits WHISPER_TIMEOUT but Whisper finishes afterward, the transcript is still saved to Redis (same subtitle cache keys as a normal success) so the next request for that video can be a cache hit. Implemented via deduplicated in-flight jobs in src/whisper-jobs.ts (startOrReuseWhisperJob), Promise.race against getWhisperConfig().timeout in src/validation.ts for auto-discovery and explicit type/lang flows, and optional timeoutMs on transcribeWithWhisper / local+API helpers (0 = no fetch abort).
  • WHISPER_BACKGROUND_TIMEOUT: Env var for the long-running Whisper HTTP client used by background jobs (unset = max(1800000, 3 × WHISPER_TIMEOUT); 0 = no client-side abort). Documented in docs/configuration.md, docs/caching.md, .env.example, and docker-compose.example.yml.
  • Prometheus gauge whisper_background_jobs_active: Tracks in-flight deduplicated background Whisper jobs; setWhisperBackgroundJobsActive() in src/metrics.ts.
  • Tests: src/whisper-jobs.test.ts; src/whisper.test.ts asserts fetch is called without signal when timeoutMs === 0; src/validation.test.ts covers cache.set after simulated timeout for auto-discover and explicit lang.

Changed

  • WHISPER_TIMEOUT semantics (docs): Clarified as the per-request wait before returning 404 to the client; background transcription may continue for cache population when Redis is enabled.
  • docs/monitoring.md: Documented whisper_background_jobs_active for API and MCP metrics tables.

v0.6.7

14 Mar 20:38

Choose a tag to compare

Added

  • VTT word-by-word deduplication: parseVTT now groups and deduplicates consecutive cues with identical text, fixing duplicated words in word-level VTT subtitles (e.g. from YouTube auto-generated captions).
  • Reddit support: Reddit (reddit.com, old.reddit.com, v.redd.it) added as a supported platform for video transcripts and metadata. Documentation, validation, and MCP tool descriptions updated.
  • extractPlatformFromUrl(): New helper extracts platform identifier from input URL hostname (youtube, reddit, vimeo, etc.) for flexible source reporting.

Changed

  • Subtitle validation and download logic: Refactored validateAndDownloadSubtitles — introduced throwNoSubtitlesError for centralized "subtitles not found" handling; split auto-discovery and explicit-request flows into handleAutoDiscoverFlow and handleExplicitRequestFlow for clearer structure and maintainability.
  • Error messages: Improved guidance for subtitle availability and Whisper fallback attempts when subtitles are not found.
  • README and documentation: Refined introduction and connection options; added "supported platforms" section emphasizing multi-platform support; clarified Whisper fallback and Redis caching in docs/configuration.md; streamlined quick-start with Smithery and Glama no-install options.
  • source field: Response schemas and validation now accept generic strings for source (e.g. youtube, whisper, reddit) instead of fixed literals, allowing new platforms without schema changes.
  • Publish Docker workflow: Extracts and outputs built image tags; removed unused release trigger.

0.6.5

13 Mar 16:33

Choose a tag to compare

Added

  • Publish Docker workflow: Manual run via workflow_dispatch with optional version input (e.g. 0.6.5 or v0.6.5) and latest_only flag. When latest_only=true, builds from default branch and pushes only :latest (no version tag).
  • Docker image verification: Publish workflow logs the yt-dlp version installed in the built image for easier debugging.
  • docs/configuration.md: Section on container memory limits for long Whisper transcriptions — deploy.resources.limits.memory (e.g. 4–6 GB) to avoid OOM kills on CPU.

Changed

  • WHISPER_TIMEOUT default: Increased from 2 minutes (120000 ms) to 10 minutes (600000 ms) to better support long videos.
  • 404 error messages: When Whisper fallback fails, the "Subtitles not found" response now explicitly mentions that Whisper was attempted and suggests increasing WHISPER_TIMEOUT (e.g. 3600000 for 1-hour videos).
  • Whisper error logging: Local and API modes now distinguish timeout (AbortError) vs network/service error in log messages.
  • docs/configuration.md: Updated WHISPER_TIMEOUT description and flow text for 1-hour videos on CPU; .env.example and docker-compose.example.yml use 600000 as the example value.

0.6.4

17 Feb 02:04

Choose a tag to compare

Added

  • MCP tool get_playlist_transcripts: Fetch cleaned subtitles for multiple videos from a playlist in one call. Parameters: url (playlist or watch with list=), optional playlistItems (yt-dlp -I spec, e.g. "1:5", "1,3,7", "-1"), maxItems, type, lang. New downloadPlaylistSubtitles() in src/youtube.ts; tool registered in mcp-core.ts and server card.
  • Configurable subtitle format (srt, vtt, ass, lrc): New env YT_DLP_SUB_FORMAT and optional format parameter for MCP tools get_transcript, get_raw_subtitles, and get_playlist_transcripts. REST API POST /subtitles accepts format. Default remains srt. Cache keys include format. Exported SubtitleFormat and resolveSubtitleFormat() in src/youtube.ts; get_raw_subtitles output schema and server card include ass and lrc.
  • search_videos extended with yt-dlp filters: Optional dateBefore (e.g. "now-1year"), date (exact date), matchFilter (e.g. "!is_live", "duration < 3600"). searchVideos() in src/youtube.ts now accepts these in SearchVideosOptions; yt-dlp receives --datebefore, --date, --match-filter when set.
  • search_videos extended: Optional offset (pagination), uploadDateFilter (hour | today | week | month | year), and response_format (json | markdown). searchVideos() in src/youtube.ts now accepts SearchVideosOptions (offset, dateAfter); yt-dlp receives --dateafter when filter is set. Server card and README tool reference updated.
  • Dynamic transcript resource: New MCP resource template transcriptor://transcript/{videoId}. Clients can read a video transcript by URI (e.g. transcriptor://transcript/dQw4w9WgXcQ) without calling a tool. Uses ResourceTemplate from the MCP SDK; handler fetches and parses subtitles and returns JSON (videoId, type, lang, text, optional source).
  • MCP prompt search_and_summarize: New prompt with args query (required) and url (optional). Builds a user message that asks the model to search YouTube for the query and summarize the first result's transcript, or to summarize the given video URL. Exposed in server card and in transcriptor://info.
  • Discoverable info resource: New MCP resource transcriptor://info (Smithery discoverable) returning JSON with server message, availableResources (info, transcript template, supported-platforms, usage), tools, and prompts. Registered in mcp-core.ts and listed in server card.
  • Use-case documentation: Four new guides in docs/: IDE and AI assistants (Cursor, Claude, VS Code), No-code automation (n8n), Researchers and batch processing, Self-hosted and enterprise. Earlier guides: summarize video, search and transcript. Linked from main README and docs/README.md.
  • 404 response available field: When subtitles are not found, the API now returns available: { official, auto } in the 404 payload so clients can show supported languages without an extra /subtitles/available call.
  • GET /changelogs: REST API and MCP HTTP servers now expose GET /changelogs, returning CHANGELOG.md as text/markdown for programmatic access.
  • CORS for MCP HTTP discovery: @fastify/cors enabled for MCP HTTP server (origin: true, methods: GET). Allows Smithery and other registries to fetch /.well-known/mcp/server-card.json and /.well-known/mcp/config-schema.json from cross-origin requests (SEP-1649).
  • SEP-1649 server card fields: Server card now includes $schema, version, protocolVersion, transport (streamable-http /mcp), and capabilities. Improves compatibility with MCP Server Cards spec and Smithery tool discovery.
  • Quick Start reordered: Smithery URL is now the first option ("no install"); Docker and local Node follow. Explicit "Connect by URL — no local install" messaging. README links to Smithery server page in header, Quick Start, Features, and "When to use".
  • Smithery badge and VS Code install badges (README): Smithery badge added to the badge row; Overview now states "Optimized for Smithery with resources, prompts, and flexible configuration". Quick Start includes one-click install badges for VS Code and VS Code Insiders (URL-based config for the Smithery server).
  • README "When to use Transcriptor MCP": New section describing when to choose transcriptor-mcp (transcripts/metadata without downloads, multi-platform, Whisper fallback, remote/HTTP, monitoring).
  • yt-dlp --no-playlist for single-video tools: downloadSubtitles, fetchYtDlpJson, and downloadAudio now pass --no-playlist so URLs like watch?v=X&list=Y process only the single video instead of the full playlist.
  • yt-dlp env filters: New optional env vars: YT_DLP_MAX_FILESIZE (e.g. "50M") for Whisper audio; YT_DLP_DOWNLOAD_ARCHIVE (path) and --break-on-existing for get_playlist_transcripts; YT_DLP_AGE_LIMIT for search_videos. Documented in docs/configuration.md and .env.example.
  • YT_DLP_AUDIO_TIMEOUT: Separate timeout for audio download (Whisper fallback). Falls back to YT_DLP_TIMEOUT when unset. Enables processing videos up to 5 hours at slow download speeds (e.g. at ~420 KiB/s, 5 h audio needs ~15 min; set 900000 ms). Documented in docs/configuration.md and .env.example.
  • Optimal audio quality for Whisper: When downloading audio via yt-dlp for Whisper fallback, the app now prefers smaller streams to reduce download time without hurting speech recognition. Format selector bestaudio[abr<=192]/bestaudio (prefer streams ≤192 kbps; fallback to best audio) and --audio-quality 5 (~128 kbps VBR for m4a) are used by default. Configurable via YT_DLP_AUDIO_FORMAT and YT_DLP_AUDIO_QUALITY (0–9, default: 5). Documented in docs/configuration.md and .env.example.
  • Sentry Performance / tracing: Optional SENTRY_TRACES_SAMPLE_RATE (0–1, default 0.1) and SENTRY_SEND_DEFAULT_PII env vars. Performance monitoring is active when running via start / start:mcp / start:mcp:http. Documented in docs/sentry.md and .env.example.
  • Sentry: beforeSend filters out expected client errors (NotFoundError, ValidationError) to reduce noise; expected 404s are monitored via Prometheus instead.
  • Prometheus metric http_404_expected_total: Counter for expected 404 responses (NotFoundError) with labels method and route.
  • Load scenario "10 VU, 1 min": New k6 script load/ten-users-1min.js — 10 concurrent users, each requests one video at a time until 1 minute; uses VIDEO_POOL. Make target load-test-10vu-1min, npm script load-test:10vu-1min. Documented in load/load-testing.md with thresholds.
  • Load scenario "100 VU, 2h podcasts": New k6 script load/podcast-2h-100vu.js — 100 VU at once, one 2h podcast per VU; uses PODCAST_2H_POOL and getPodcast2hRequest() in load/config.js. Make target load-test-podcast-2h, npm script load-test:podcast-2h.
  • Load test result report: load/load-test-result-2025-02-15.md — results for subtitles, mixed, 10 VU 1 min, and podcast 2h scenarios (k6, BASE_URL, thresholds, metrics).
  • yt-dlp retries and extra args (all calls): YT_DLP_RETRIES (-R), YT_DLP_RETRY_SLEEP (e.g. linear=1::2), YT_DLP_EXTRA_ARGS (space-separated). Documented in docs/configuration.md and .env.example.
  • yt-dlp sleep options (rate limits): YT_DLP_SLEEP_REQUESTS, YT_DLP_SLEEP_INTERVAL, YT_DLP_MAX_SLEEP_INTERVAL, YT_DLP_SLEEP_SUBTITLES. Documented in docs/configuration.md and .env.example.
  • yt-dlp subtitle encoding: YT_DLP_ENCODING (e.g. utf-8, cp1251) for subtitle downloads (--encoding).
  • yt-dlp audio download options (Whisper only): YT_DLP_AUDIO_CONCURRENT_FRAGMENTS, YT_DLP_AUDIO_LIMIT_RATE, YT_DLP_AUDIO_THROTTLED_RATE, retries, buffer, downloader args for DASH/HLS reliability and speed. Documented in docs/configuration.md and .env.example.
  • YT_DLP_NO_WARNINGS: When set to 1, pass --no-warnings to all yt-dlp calls. Reduces log noise.
  • YT_DLP_IGNORE_NO_FORMATS: When not set to 0, pass --ignore-no-formats-error when fetching video metadata (info, chapters, available subtitles), so region-locked or undownloadable videos still return metadata. Set to 0 to fail on "No video formats" (default yt-dlp behavior). Documented in docs/configuration.md.
  • Unit tests for Tool Quality: In mcp-http.test.ts, tests for server card: "includes title for each tool (Tool Quality)" asserts every tool has the expected title; "includes parameter descriptions for get_raw_subtitles (Tool Quality)"; "includes SEP-1649 fields" for $schema, version, protocolVersion, transport, and capabilities.

Changed

  • README: Use-case section now references docs/README.md with the full list of guides (summarize video, search and transcript, IDE/Cursor/Claude, n8n, researchers/batch, self-hosted).
  • Tool Quality (Smithery): Server card now includes title for every tool (e.g. "Get video transcript", "Get raw video subtitles") and description for every parameter of get_raw_subtitles (type, lang, response_limit, next_cursor). In mcp-core.ts, optional fields of subtitleInputSchema now have .describe() so live MCP tools/list returns parameter descriptions. Improves Smithery Tool Quality score.
  • Error messages: NotFoundError messages now hint at /subtitles/available and auto-discovery (omit type/lang).
  • README Features: First bullet is "Connect by URL (Smithery)" — use the server without installing Docker or Node. MCP quick start section retitled to "Docker and self-hosted" with a pointer to Smithery for one-click connection.
  • MCP config schema and .well-known/mcp-config: Enriched documentation with expanded gettingStarted (three steps including Smithery URL and tool names), apiLink (GitHub readme), and updated security...
Read more

v0.5.5: Update to version 0.5.5 and add `search_videos` tool

15 Feb 01:51

Choose a tag to compare

Added

  • MCP tool search_videos: Search videos on YouTube via yt-dlp (ytsearch). No required parameters; provide query and optional limit (default 10, max 50). Returns list of videos with metadata (id, title, url, duration, uploader, viewCount, thumbnail). New searchVideos(query, limit, log) in src/youtube.ts; tool registered in mcp-core.ts and exposed in server card.
  • Sentry breadcrumbs from Pino logs: When a 4xx or 5xx error is sent to Sentry, the event now includes a full trail of log calls (debug, info, warn, error) that led up to the error. REST API and MCP HTTP use a Pino logger that writes each log line to stdout and adds a Sentry breadcrumb; maxBreadcrumbs set to 100 in Sentry init. New module src/logger-sentry-breadcrumbs.ts (createLoggerWithSentryBreadcrumbs()); docs/sentry.md updated with a Breadcrumbs section.
  • MCP_PUBLIC_URLS: Comma-separated list of public base URLs for multi-origin MCP deployments (e.g. Smithery + direct domain). The server selects the matching URL per request using Host or X-Forwarded-Host. When set, takes precedence over MCP_PUBLIC_URL. Backward compatible: single MCP_PUBLIC_URL still works.
  • POST /sse compatibility: Some MCP clients (e.g. Cursor via Smithery) POST to /sse for streamable HTTP. The server now accepts POST on /sse and delegates to the streamable handler; canonical endpoint remains POST /mcp.

0.4.8

13 Feb 04:17

Choose a tag to compare

Added

  • Unit tests: ensureWritableCookiesFile — returns original path when writable; copies to temp and cleans up when read-only.
  • CI (GitHub Actions): .github/workflows/ci.yml runs on push/PR to main: npm ci, make check-no-smoke (format-check, lint, typecheck, test, build). On push to main, optional smoke job runs REST API smoke with SMOKE_SKIP_MCP=1. .github/workflows/publish-docker.yml runs on tag push v*: build and push REST API and MCP images to Docker Hub (multi-arch linux/amd64, linux/arm64). Requires DOCKERHUB_USERNAME and DOCKERHUB_TOKEN secrets.
  • Readiness and metrics (REST API): GET /health/ready — when CACHE_MODE=redis, pings Redis; returns 503 if Redis is unreachable (for Kubernetes readiness). GET /metrics — Prometheus text format with counters: http_requests_total, http_request_errors_total, cache_hits_total, cache_misses_total. New src/metrics.ts; validation layer records cache hit/miss; REST error handler and onResponse hook record errors and requests.
  • Cache: cache.ping() in src/cache.ts for Redis liveness. Unit tests for ping() when cache off and when Redis URL unset.
  • Documentation: README — repo/package name note (yt-captions-downloader vs transcriptor-mcp), Versioning subsection (version from package.json, tagging), Security section (do not commit or log WHISPER_API_KEY, CACHE_REDIS_URL, MCP_AUTH_TOKEN, cookies path; use env or secret manager). docs/configuration.md — Health and metrics (health, health/ready, metrics), Recommended values for production table. docs/caching.md — section “When Redis is unavailable” (graceful degradation: request still served via yt-dlp).
  • E2E smoke: MCP streamable HTTP smoke now includes checkMcpStreamableGetTranscript: after initialize, calls tools/call for get_transcript and asserts content or structuredContent. load/load-testing.md — “Recommended thresholds for regression” (e.g. http_req_failed rate<0.05, p95<120s; k6 run --throw for CI).
  • Pre-commit (Husky): husky devDependency and prepare script; .husky/pre-commit runs npm run format-check && npm run lint.
  • verify-pool script: npm run verify-pool (and Make target) runs load/verify-pool.js to validate the k6 load-test video ID pool.
  • Optional Redis cache: Responses for subtitles, video info, available subtitles, and chapters can be cached in Redis to reduce repeated yt-dlp calls. Configure via env: CACHE_MODE (off or redis), CACHE_REDIS_URL (required when redis), CACHE_TTL_SUBTITLES_SECONDS (default 7 days for subtitles), CACHE_TTL_METADATA_SECONDS (default 1 hour for video info, available subtitles, chapters). New src/cache.ts with getCacheConfig(), get(), set(), close(). Both REST API and MCP use the cache when enabled. Documented in docs/caching.md, docs/configuration.md, and .env.example.
  • MCP uses validation layer: MCP tools now call validateAndDownloadSubtitles, validateAndFetchAvailableSubtitles, validateAndFetchVideoInfo, and validateAndFetchVideoChapters instead of calling youtube/whisper directly, so MCP benefits from the same cache and validation as the REST API. Removed private fetchSubtitlesContent from mcp-core.ts; tools catch ValidationError and NotFoundError and return tool errors.
  • Unit tests: cache.test.ts for getCacheConfig (mode, TTLs from env), get/set when CACHE_MODE=off, and close(). validation.test.ts mocks ./cache.js so existing tests run with cache disabled. mcp-core.test.ts updated to mock validation’s validateAnd* and expect corresponding calls.
  • REST/MCP error types: src/errors.ts exports HttpError, ValidationError, and NotFoundError with status codes and error labels. Validation helpers throw these; REST global error handler maps them to 4xx/5xx and consistent JSON (error, message).
  • MCP HTTP auth module: src/mcp-auth.ts provides ensureAuth(request, reply, authToken) and getHeaderValue(); MCP HTTP server uses them when MCP_AUTH_TOKEN is set. Token comparison is timing-safe to prevent timing attacks.
  • Unit tests: mcp-auth.test.ts for getHeaderValue and ensureAuth (no auth, missing/ malformed Bearer, wrong token, correct token). mcp-http.test.ts for 401 on /mcp when auth required and no/ invalid header, and that /health remains allowed without auth when token is set.
  • Load testing: docs/load-testing.md documents k6-based load tests for the REST API (health, subtitles, mixed). Make targets: load-test, load-test-health, load-test-subtitles, load-test-mixed (Docker k6); npm scripts: load-test, load-test:subtitles, load-test:mixed. Configurable via LOAD_BASE_URL / BASE_URL and RATE_LIMIT_MAX for throughput.
  • Export: YtDlpVideoInfo type is now exported from youtube.ts for callers that pass pre-fetched data into fetchVideoChapters.
  • Unit tests: youtube.test.tsfetchVideoChapters with preFetchedData (no execFile call, correct chapter mapping; null handling). validation.test.tsfetchYtDlpJson called once and data passed to fetchVideoChapters; Vimeo test expects three-argument call. mcp-core.test.ts — chapters tool expectations updated for fetch order and three-argument fetchVideoChapters call.
  • yt-dlp proxy (optional): All yt-dlp requests (subtitle download, video info, chapters, audio for Whisper) can be routed through a proxy. Set YT_DLP_PROXY to a URL; supported schemes: http://, https://, socks5:// (e.g. http://user:password@proxy.example.com:8080, socks5://127.0.0.1:9050 for Tor). Documented in docs/configuration.md and .env.example; in Docker, set the variable in the container environment if needed.
  • Unit tests: mcp-core.test.ts — Whisper fallback with omitted lang (auto-detect). whisper.test.ts — no language param when lang is empty.
  • yt-dlp cookies file logging: When COOKIES_FILE_PATH is set, the app now logs cookies file status before each yt-dlp call (subtitle download, audio download, video info/chapters). Logs include path, existence, file size, or access error message (no cookie contents). Helps diagnose "Sign in to confirm you're not a bot" and other YouTube auth issues when running in Docker or with mounted cookies.

Changed

  • Graceful shutdown: REST API (src/index.ts) and MCP HTTP (src/mcp-http.ts) now call closeCache() after closing the server so the Redis connection is closed cleanly.
  • yt-dlp-check: Fallback logger uses console.warn instead of console.info for the info-level message to satisfy the no-console lint rule.
  • Dependencies: Bumped Fastify plugins (@fastify/cors ^11.2.0, @fastify/multipart ^9.4.0, @fastify/rate-limit ^10.3.0, @fastify/swagger ^9.7.0, @fastify/swagger-ui ^5.2.5, @fastify/type-provider-typebox ^6.1.0), @sinclair/typebox ^0.34.48, ioredis ^5.9.3. Dev: @types/jest ^30.0.0, @types/node ^25.2.3, @typescript-eslint/* and typescript-eslint ^8.55.0, eslint ^9.18.0, jest ^30.2.0, prettier ^3.8.1, ts-jest ^29.4.6, typescript ^5.9.3, husky ^9.1.7.
  • Dependency: Added ioredis for Redis cache backend (used only when CACHE_MODE=redis).
  • MCP: Shared logic for subtitle fetch and Whisper fallback is now in a private fetchSubtitlesContent(resolved, log) in mcp-core.ts. Tools get_transcript and get_raw_subtitles call it and only handle final processing (parse + paginate vs raw + paginate). Removes duplication of resolveSubtitleArgs, downloadSubtitles, and Whisper fallback between the two tools.
  • Docker: single Dockerfile with shared base. One Dockerfile now builds both REST API and MCP images via multi-stage build. Stages: builder (Node, npm ci, build) → base (node, python3, pip, curl, unzip, ffmpeg, Deno, yt-dlp -U, YT_DLP_JS_RUNTIMES) → api (REST, port 3000) and mcp (MCP, port 4200). Build with docker build -f Dockerfile --target api . or --target mcp .. Dockerfile.mcp removed; Makefile targets docker-build-api and docker-build-mcp (and buildx variants) use the same Dockerfile with the appropriate target. README and docs/quick-start.mcp.md updated to use --target api / --target mcp.
  • Chapters: single yt-dlp fetch. validateAndFetchVideoChapters (REST /video-info/chapters) and MCP tool get_video_chapters now perform one yt-dlp network call instead of two. fetchVideoChapters in youtube.ts accepts an optional third argument preFetchedData; when provided, it reuses that data and skips the internal fetchYtDlpJson call. Validation and MCP handlers fetch once and pass the result into fetchVideoChapters, so video ID and chapters are derived from the same response.
  • Unit tests: youtube.test.tsgetYtDlpEnv and appendYtDlpEnvArgs now cover YT_DLP_PROXY / proxyFromEnv (trim, presence of --proxy in args, omission when unset).
  • MCP tools get_transcript and get_raw_subtitles: Parameter lang is now optional. When omitted, subtitle download still uses en for yt-dlp; when Whisper fallback is used, language is auto-detected (no language query param sent to Whisper). Tool descriptions updated to mention optional lang and auto-detect behavior.

Security

  • MCP HTTP auth: Bearer token validation uses crypto.timingSafeEqual so comparison time does not depend on the token value.

Fixed

  • yt-dlp cookies on read-only volume: When COOKIES_FILE_PATH points to a read-only file (e.g. Docker volume mounted without write access), yt-dlp failed with PermissionError while saving cookies at exit, even when the download succeeded. The app now copies the cookies file to a writable temp location before passing it to yt-dlp; the temp file is removed after each call. New ensureWritableCookiesFile() in youtube.ts checks read/write access and returns either the original path or a temp copy. Used by `downloadSubti...
Read more

transcriptor-mcp

12 Feb 19:19

Choose a tag to compare

Changed

  • Project rename: yt-captions-downloadertranscriptor-mcp. Package name, GitHub repo, Docker images, and docker-compose service names have been updated.
  • Package: transcriptor-mcp (was yt-captions-downloader-mcp).
  • GitHub: samson-art/transcriptor-mcp.
  • Docker images: artsamsonov/transcriptor-mcp (MCP), artsamsonov/transcriptor-mcp-api (REST API).
  • docker-compose services: transcriptor-mcp (MCP), transcriptor-mcp-api (REST API).
  • MCP server name: transcriptor-mcp (reported in MCP initialize).
  • User-Agent: transcriptor-mcp (for yt-dlp requests).
  • MCP config key: Use transcriptor in claude_desktop_config.json / Cursor MCP settings (shorter UX).
  • docs/configuration.md: Documented YT_DLP_SKIP_VERSION_CHECK and YT_DLP_REQUIRED; startup checks reference src/yt-dlp-check.ts. Added "Whisper fallback" section for all WHISPER_* variables and usage (local container vs API).

Added

  • Multi-platform support: Subtitles, available subtitles, video info, and chapters work with URLs from YouTube, Twitter/X, Instagram, TikTok, Twitch, Vimeo, Facebook, Bilibili, VK, and Dailymotion (via yt-dlp). Bare video IDs are supported for YouTube only.
  • Whisper fallback: When YouTube subtitles cannot be obtained (yt-dlp returns none), the app can transcribe video audio via Whisper. Configurable with WHISPER_MODE (off, local, api). Local mode uses a self-hosted HTTP service (e.g. whisper-asr-webservice in Docker); API mode uses an OpenAI-compatible transcription endpoint. New env vars: WHISPER_BASE_URL, WHISPER_TIMEOUT, WHISPER_API_KEY, WHISPER_API_BASE_URL. REST responses for /subtitles and /subtitles/raw include optional source: "youtube" | "whisper"; MCP tools get_transcript and get_raw_subtitles use the same fallback and expose source in structured content.
  • Audio download: downloadAudio(videoId, logger) in youtube.ts downloads audio-only via yt-dlp for Whisper input; uses same cookies and timeout as subtitle download.
  • Docker: docker-compose.example.yml adds a whisper service (image onerahmet/openai-whisper-asr-webservice:latest) and example WHISPER_* env for transcriptor-mcp-api and transcriptor-mcp. .env.example and docs/configuration.md document all Whisper options.
  • Unit tests: src/whisper.test.ts for getWhisperConfig and transcribeWithWhisper; validation.test.ts extended with Whisper fallback success and 404 when Whisper returns null.
  • yt-dlp startup check: REST API, MCP HTTP, and MCP stdio servers run a yt-dlp availability check at startup. If yt-dlp is missing or fails to run, the app logs an ERROR and exits (unless YT_DLP_REQUIRED=0). If the installed version is older than the latest on GitHub, a WARNING is logged.
  • Environment variables: YT_DLP_SKIP_VERSION_CHECK — when set to 1, skips the GitHub version check and WARNING; YT_DLP_REQUIRED — when set to 0, logs ERROR but does not exit when yt-dlp is missing or fails.
  • Unit tests: src/yt-dlp-check.test.ts for version parsing, comparison, GitHub fetch, and startup check behavior.