mcp-xtream
Saves downloaded catchup programmes with Jellyfin-compatible naming (e.g. 'Show/Season 02/Show - S02E05 - Title.mkv' or air-date naming) and writes matching .nfo metadata files, so recordings drop straight into a Jellyfin library.
Downloads past programmes from an IPTV provider's catchup archive and names them for a Plex library (show/season/episode or date-based naming) so they can be picked up and indexed by Plex.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-xtream Download last night's Gogglebox for my Jellyfin library"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-xtream
An MCP server for Xtream Codes IPTV subscriptions. It lets an AI assistant search your provider's TV guide and download past programmes from its catchup archive, saved ready for Jellyfin or Plex.
You: Download Sunday Politics from this morning, BBC One NI.
Assistant: Found it: Sunday Politics, 10:00–10:30 on IRL: BBC ONE NORTHERN 4K (catchup available). Downloading in the background… done:
~/Movies/IPTV/Sunday Politics/Sunday Politics - 2026-10-18.ts(1.4 GB, 30:25).
Other things to ask:
"Is there any snooker in the last three days?"
"What's on RTÉ One tonight?"
"Get last night's Gogglebox for my Jellyfin library."
"How long is left on my IPTV subscription?"
Features
Guide search across thousands of channels, past and upcoming, with every channel carrying a programme and whether it can still be downloaded.
Background catchup downloads that keep going after the chat ends, resume dropped connections from the exact byte, and save the provider's MPEG-TS untouched (Jellyfin and Plex play it as is), or
.mkvon request.Jellyfin/Plex naming (
Show/Season 02/Show - S02E05.tsplus an.nfofile) when the assistant has identified the episode, or date-based naming when it hasn't.Handles provider quirks: catchup times in the server's own timezone, "+1" channels that reuse the main channel's guide, duplicated or overlapping guide entries, and guides that only reach a day back (past programmes are kept for 7 days so the whole catchup window stays searchable).
Tells you when a download is done: clients that support server-push events get a
download_finished/download_failednotification instead of having to poll.Respects your connection limit: won't start a download while all of your account's connections are in use (e.g. you're watching TV).
Keeps your password private: stream URLs contain it, so no tool ever returns one, and errors never include it.
Related MCP server: XMLTV MCP Server
Quick start
You need uv.
uvx downloads and runs the server in an isolated environment, fetching
Python 3.13 if needed; nothing else to install. ffmpeg is optional (only
for .mkv output).
Claude Code
claude mcp add xtream \
-e XTREAM_SERVER=http://your-provider.example:80 \
-e XTREAM_USERNAME=your-username \
-e XTREAM_PASSWORD=your-password \
-- uvx --from git+https://github.com/dmarkey/mcp-xtream mcp-xtreamClaude Desktop and other MCP clients
Add to your client's MCP configuration (for Claude Desktop,
claude_desktop_config.json):
{
"mcpServers": {
"xtream": {
"command": "uvx",
"args": ["--from", "git+https://github.com/dmarkey/mcp-xtream", "mcp-xtream"],
"env": {
"XTREAM_SERVER": "http://your-provider.example:80",
"XTREAM_USERNAME": "your-username",
"XTREAM_PASSWORD": "your-password"
}
}
}
}Try it from a terminal
XTREAM_SERVER=http://your-provider.example:80 \
XTREAM_USERNAME=your-username XTREAM_PASSWORD=your-password \
uvx --from git+https://github.com/dmarkey/mcp-xtream mcp-xtreamIt speaks MCP over stdio, so it waits silently for a client; use MCP Inspector to poke at the tools by hand.
Configuration
Everything is set with environment variables.
Variable | Required | Default |
| yes | — e.g. |
| yes | — |
| yes | — |
| no |
|
| no |
|
| no | your system timezone, e.g. |
The -e / env settings above store your password in plain text in the
client's config file. If you'd rather not, export the variables from your
shell profile instead and leave them out of the MCP config.
Tools
Tool | What it does |
| Search the guide by title (optionally descriptions or channel), past, now or upcoming. Lists every channel carrying each programme and whether catchup is available |
| Find channels by name or category, with catchup days and what's on now |
| One channel's guide for a day |
| Ask the provider whether a channel's recording of a programme actually exists right now (briefly uses a connection) |
| Full description, air times and channels for one programme, for identifying the episode |
| Download a past programme in the background, optionally named for Jellyfin/Plex |
| Track and manage downloads |
| Get pushed a notification when each download finishes, fails or is cancelled |
| Subscription status, expiry, connection limit, active connections |
| Re-fetch the channel list and guide now |
Catch-up availability is a hint
Every channel stays listed (channels without catch-up are still useful for
live TV). Each programme lists the channels carrying it, best bet first, with
a catchup_status:
Status | Meaning |
| Within the channel's advertised catch-up window |
| Ended in the last few hours; the provider may not have the recording yet |
| Not finished yet |
| Older than the channel's archive |
| The channel has no archive (live only) |
The advertised window is only a hint: one channel's recording can be missing
while another channel carrying the same programme has it, and providers
sometimes lose recordings early. check_recording asks the provider directly
(a 1 KB request, refused while your connections are all in use), and the
assistant is told to try other channels before giving up.
Download notifications
Clients that support server-push events can call subscribe_events once after
connecting. Each download that ends is then announced as an MCP log
notification with logger="events":
{"event": "download_finished", "job_id": "c7cba935dee2", "title": "Sunday Politics",
"channel": "IRL: BBC ONE NORTHERN 4K", "output": "/media/tv/Sunday Politics/Sunday Politics - 2026-10-18.ts",
"size_mb": 1288.6, "message": "Download finished: 'Sunday Politics' (1.3 GB) saved to …"}download_failed (with error) and download_cancelled work the same way.
Announcements are tracked per client on disk, so a download that ends while
the server is restarting is still announced when the client resubscribes.
For markeybot, set "events": true on
the server entry and it subscribes automatically.
Note: MCP's logging capability, which these notifications ride on, is marked
deprecated in the 2026-07-28 spec (SEP-2577). Clients that still deliver log
notifications (the MCP Python SDK's ClientSession with a logging_callback,
markeybot) work; others just won't see the events, and download_status still
works for everyone.
Episode numbers are best effort (and the assistant's job)
Xtream guides almost never carry season/episode numbers (one provider tested
had them for 0.1% of programmes), and repeats air long after the original
broadcast, so the server doesn't guess. Instead the assistant reads the
description with programme_details, identifies the episode itself (web
search, TheTVDB, TMDB, TVmaze or the broadcaster's site) and passes what it
found to download_programme:
Arguments | Saved as |
|
|
|
|
neither |
|
Treat the numbers as a best guess: broadcasters and databases often number the
same episode differently (e.g. "series 18" vs "season 28"), so tell the
assistant which database your media server uses. The .nfo file records the
description and air date and notes that the numbering came from the assistant.
How it works
The guide. The channel list and XMLTV guide are fetched from your
provider and cached on disk (refreshed when over 6 hours and 3 hours old
respectively, the next time a tool is used; refresh_data forces it).
Overlapping entries are resolved, mangled titles cleaned up, and past
programmes kept for 7 days. If a refresh fails, the previous copy is used and
results carry a warning.
"+1" channels. Providers often point "+1" channels at the main channel's guide. When a channel's name contains "+1" but its guide doesn't, its times are shifted an hour later everywhere, so the right hour is downloaded.
Downloads. Each download runs as a detached process, so it survives the
assistant session ending, and holds a lock that the operating system releases
however it exits, so a crashed download never blocks the next one. Catchup URLs
use the start time in the provider's timezone (many providers aren't on
UTC). Dropped connections resume with an HTTP Range request from the last byte
written. Only one download runs at a time, and cancelling also stops an
in-progress .mkv conversion and removes partial files.
File format. Recordings are saved as the MPEG-TS the provider sends,
byte for byte (minus a partial packet at the start). format="mkv" remuxes
with ffmpeg instead (no re-encode, keeps subtitles), except for interlaced
video: broadcast 1080i often codes each field as a separate picture, which
Matroska stores with out-of-order timestamps that play back as corruption,
so those recordings stay .ts and the job carries a warning saying why.
Requirements and limitations
macOS or Linux (downloads use POSIX file locking; Windows isn't supported).
Python 3.13+ (
uvxfetches it automatically).Your provider must support Xtream Codes catchup (
tv_archive) for downloads; how far back depends on the channel (often 1–7 days).A download uses one of your account's connections. With a one-connection account you can't watch and download at the same time.
Development
git clone https://github.com/dmarkey/mcp-xtream
cd mcp-xtream
uv sync
uv run pytest # unit + end-to-end tests against local fake servers
uv run ruff check src testsThe tests never contact a real provider.
Disclaimer
This is an unofficial client for the Xtream Codes API. Use it only with IPTV services you are entitled to use, and respect your provider's terms and the rights of content owners. Downloads are for personal, time-shifted viewing.
License
Available Tools
12 toolsaccount_infoAccount InfoA
Account status: expiry, connection limit and current active connections (fetched live).
Inputs: no named parameters. Returns: object
cancel_downloadCancel DownloadA
Cancel a running download (including its MKV conversion) and delete its partial file. Poll download_status to see it reach 'cancelled'.
Inputs: job_id (required). Returns: object
channel_scheduleChannel ScheduleC
A channel's guide for one day. date: 'today', 'yesterday', 'tomorrow' or YYYY-MM-DD (local time).
Inputs: stream_id (required), date, include_descriptions. Returns: object
check_recordingCheck RecordingA
Ask the provider whether this channel's catch-up recording of a programme exists right now (the advertised window is only a hint).
Opens the recording briefly (a 1 KB request) and reports whether it's there and how big it is. That uses one of the account's connections for a moment, so it refuses while they're all in use (e.g. the user is watching TV) unless force=True, and while a download is running.
Inputs: stream_id (required), programme_id (required), force. Returns: object
download_programmeDownload ProgrammeA
Download a past programme from a channel's catchup archive in the background. Returns a job_id; poll download_status.
Identify the programme with programme_id (from search_programmes / channel_schedule), or give start/end as ISO local datetimes (e.g. 2026-10-18T10:00) plus a title. The default format 'ts' saves the provider's stream untouched (Jellyfin/Plex play it). 'mkv' remuxes with ffmpeg (no re-encode, keeps subtitles) only if the user asks for it; it stays .ts if ffmpeg is missing or the video is interlaced (fields don't convert cleanly), with a warning on the job. force=True skips the free-connection check. Providers often take a few hours after a programme ends to make its recording available; a "not ready yet" failure means try again later.
Media-server naming (optional): give show (the series name as your media server knows it) to save as Jellyfin/Plex TV layout with an .nfo file: with season+episode -> 'Show/Season 02/Show - S02E05 - <episode_title>', without them -> date-based 'Show/Show - 2026-10-18'. season/episode are your best-effort identification (see programme_details); only pass them when confident. Without show, files are named ' - - '.
Inputs: stream_id (required), end, show, force, +7 more. Returns: object
download_statusDownload StatusB
Progress of a download job: state is queued, downloading, converting, done, failed or cancelled.
Inputs: job_id (required). Returns: object
list_channelsList ChannelsA
Find live channels by name or category (case-insensitive; all words must match). Each result includes stream_id, epg_id, catchup_days (0 = no catch-up archive; still fine for live TV) and what's on now. catchup_only keeps just channels with an archive.
Inputs: limit, query, catchup_only. Returns: object
list_downloadsList DownloadsC
Recent download jobs, newest first.
Inputs: limit. Returns: object
programme_detailsProgramme DetailsA
Everything the guide knows about one programme: full description, air times and every channel carrying it (with catchup availability).
Use this to identify the episode yourself before downloading; the guide has no season/episode numbers.
Inputs: programme_id (required). Returns: object
refresh_dataRefresh DataA
Re-fetch the channel list and TV guide from the provider now. They refresh automatically (guide every 3h, channels every 6h) when a tool is used; past programmes are kept for 7 days so catchup stays searchable.
Inputs: no named parameters. Returns: object
search_programmesSearch ProgrammesA
Search the TV guide by programme title (all words must match).
channel: optional channel-name filter (e.g. "bbc one"). The window is hours_back before now to hours_ahead after. Each result lists every channel carrying that guide entry, best catch-up bet first, each with a catchup_status (see server instructions) and catchup_available (true for in_window / just_aired). catchup_only keeps only channels where it's true, and drops programmes with none. Results are newest first.
Inputs: query (required), when, limit, channel, +4 more. Returns: object
subscribe_eventsSubscribe EventsA
Register this session for server-push download events.
Call once after connecting (and again after reconnecting; repeat calls are harmless). When a download finishes, fails or is cancelled, the server pushes an MCP log notification with logger="events" and a payload like {"event": "download_finished", "title": ..., "output": ..., "message": ...}. The first subscription from a client covers downloads that end from then on; later ones also catch up on any that ended while disconnected. Requires a persistent session (stdio).
Inputs: no named parameters. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 12 tools
Each tool targets a distinct step in the workflow: channel discovery (list_channels), guide lookup (search_programmes, channel_schedule, programme_details), availability probing (check_recording), and download lifecycle (download_programme, download_status, list_downloads, cancel_download). The guide-related trio (search_programmes / channel_schedule / programme_details) and the two availability signals (programme_details' catchup_available vs check_recording) are close enough that an agent may occasionally hesitate, but descriptions draw clear boundaries.
All snake_case, which is good, but the convention is mixed: about half are verb_noun (list_channels, search_programmes, download_programme, cancel_download, refresh_data) and the rest are bare noun phrases (account_info, download_status, channel_schedule, programme_details). Readable and semantically sensible, but not a predictable pattern an agent can extrapolate from.
Twelve tools for an IPTV/catch-up download server is well-scoped: discovery, EPG, availability check, download controls, event subscription and cache refresh each earn their place with no filler or redundancy.
The catch-up download lifecycle is fully covered (search → verify recording → download → status → list → cancel), plus account info, push events and cache refresh. The main gap is live playback: list_channels returns stream_ids but there is no tool to obtain a playable stream URL or otherwise watch live TV, and there is no way to delete or manage completed files.
Maintenance
Related MCP Connectors
Control Android TV from any AI. 38 MCP tools: playback, recap, recommend, smart-home, schedules.
Query your Twitch streams, events, supporters, raids & rankings from an AI assistant via OAuth.
Track the TV and films you watch, and find more like them, by talking to your own AI.
YouTube transcripts, search, channel/playlist listings and upload tracking for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA comprehensive Model Context Protocol server that bridges LLMs with self-hosted media services, enabling natural language control of TV shows, movies, downloads, and notifications while maintaining traditional API access.17AGPL 3.0
- AlicenseAqualityDmaintenanceEnables querying TV programming data from Tunarr XMLTV feeds, including viewing channel listings, current and upcoming schedules, and searching for programmes by title or description.5MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to manage media automation services like Sonarr, Radarr, Prowlarr, Bazarr, Overseerr, and Plex through natural language commands.19 PyPI8MIT
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to manage TV shows and movies via Sonarr and Radarr, including searching, adding, and monitoring downloads.-