Skip to main content
Glama

mcp-xtream

CI Python 3.13+ License: MIT

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 .mkv on request.

  • Jellyfin/Plex naming (Show/Season 02/Show - S02E05.ts plus an .nfo file) 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_failed notification 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-xtream

Claude 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-xtream

It 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

XTREAM_SERVER

yes

— e.g. http://provider.example:80 (the address your IPTV app uses)

XTREAM_USERNAME

yes

—

XTREAM_PASSWORD

yes

—

XTREAM_DOWNLOAD_DIR

no

~/Movies/IPTV (macOS), ~/Videos/IPTV (Linux)

XTREAM_CACHE_DIR

no

~/Library/Caches/mcp-xtream (macOS), ~/.cache/mcp-xtream (Linux)

XTREAM_TZ

no

your system timezone, e.g. Europe/Dublin (used to display times)

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_programmes

Search the guide by title (optionally descriptions or channel), past, now or upcoming. Lists every channel carrying each programme and whether catchup is available

list_channels

Find channels by name or category, with catchup days and what's on now

channel_schedule

One channel's guide for a day

check_recording

Ask the provider whether a channel's recording of a programme actually exists right now (briefly uses a connection)

programme_details

Full description, air times and channels for one programme, for identifying the episode

download_programme

Download a past programme in the background, optionally named for Jellyfin/Plex

download_status / list_downloads / cancel_download

Track and manage downloads

subscribe_events

Get pushed a notification when each download finishes, fails or is cancelled

account_info

Subscription status, expiry, connection limit, active connections

refresh_data

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

in_window

Within the channel's advertised catch-up window

just_aired

Ended in the last few hours; the provider may not have the recording yet

airing / not_aired_yet

Not finished yet

expired

Older than the channel's archive

no_catchup

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

show, season, episode (+ optional episode_title)

Show/Season 02/Show - S02E05 - Title.ts + .nfo

show only

Show/Show - 2026-10-18.ts + .nfo (Jellyfin's air-date naming)

neither

<title> - <channel> - <date time>.ts

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+ (uvx fetches 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 tests

The 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

MIT

Available Tools

12 tools
account_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

    B3.4/5.0

    Scored across 12 tools

    Disambiguation4/5

    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.

    Naming Consistency3/5

    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.

    Tool Count5/5

    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.

    Completeness4/5

    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

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      Enables querying TV programming data from Tunarr XMLTV feeds, including viewing channel listings, current and upcoming schedules, and searching for programmes by title or description.
      5
      MIT
    • F
      license
      Not graded
      quality
      D
      maintenance
      Enables AI assistants to manage TV shows and movies via Sonarr and Radarr, including searching, adding, and monitoring downloads.
      -