media-mcp

media-mcp

Enables users to manage Sonarr and Radarr instances through natural language, allowing search, addition, deletion, and monitoring of TV series and movies.

Category
访问服务器

README

media-mcp

Serveur MCP local (transport stdio) pour piloter un stack média self-hosted : Sonarr + Radarr, et qBittorrent via qui (autobrr).

Prérequis

  • Python 3.11+
  • uv installé

Installation

# Cloner / se placer dans le répertoire du projet
cd media-mcp

# Installer les dépendances
uv sync

# Copier et remplir les variables d'environnement
cp .env.example .env
# Éditer .env avec vos URLs et clés API

Lancement en développement

uv run python -m media_mcp

Le serveur démarre en mode stdio et attend des messages MCP sur stdin/stdout.

Configuration Claude Desktop

Ajouter dans ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) ou %APPDATA%\Claude\claude_desktop_config.json (Windows) :

{
  "mcpServers": {
    "media-mcp": {
      "command": "uv",
      "args": ["--directory", "/chemin/absolu/media-mcp", "run", "python", "-m", "media_mcp"],
      "env": {
        "SONARR_URL": "http://localhost:8989",
        "SONARR_API_KEY": "xxx",
        "RADARR_URL": "http://localhost:7878",
        "RADARR_API_KEY": "xxx",
        "QUI_URL": "https://qui.example.com",
        "QUI_API_KEY": "xxx",
        "QUI_INSTANCE": "",
        "PROWLARR_URL": "http://localhost:9696",
        "PROWLARR_API_KEY": "xxx"
      }
    }
  }
}

Remplacer /chemin/absolu/media-mcp par le chemin réel du projet.

Variables d'environnement

Variable Description Défaut
SONARR_URL URL de base Sonarr http://localhost:8989
SONARR_API_KEY Clé API Sonarr (requis)
RADARR_URL URL de base Radarr http://localhost:7878
RADARR_API_KEY Clé API Radarr (requis)
QUI_URL URL de base de l'instance qui (requis pour qBit)
QUI_API_KEY Clé API qui (Settings > API Keys) (requis pour qBit)
QUI_INSTANCE Instance qBit ciblée (id ou nom) ; vide = auto si une seule (optionnel)
PROWLARR_URL URL de base Prowlarr (requis pour Prowlarr)
PROWLARR_API_KEY Clé API Prowlarr (requis pour Prowlarr)

Tools disponibles

Sonarr

Tool Type Description
sonarr_system_status read Statut et version de Sonarr
sonarr_list_series read Liste des séries suivies
sonarr_lookup_series(term) read Recherche une série (pour ajout)
sonarr_quality_profiles read Profils de qualité disponibles
sonarr_root_folders read Dossiers racine configurés
sonarr_queue read File de téléchargement + diagnostic des items bloqués (voir ci-dessous)
sonarr_disk_space read Espace disque par volume, le plus plein en premier
sonarr_health read Avertissements de santé de l'instance
sonarr_history(limit=20, event_type=None) read Événements récents (grab/import/…) avec downloadId ; filtre event_type optionnel (voir ci-dessous)
sonarr_delete_queue_item(queue_id=None, download_id=None, remove_from_client=True, blocklist=False, confirm=False) write Retire un item (par queue_id) ou tous ceux d'un même download_id (season pack) — exactement un des deux
sonarr_upcoming(days=7) read Épisodes à venir via calendrier
sonarr_series_seasons(series_id) read Détail saison par saison d'une série
sonarr_season_episodes(series_id, season_number) read Liste les épisodes d'une saison (E-num, titre, hasFile ✓/✗, monitored ✓/✗, id, fileId)
sonarr_add_series(tvdb_id, quality_profile_id, root_folder_path, confirm=False) write Ajoute une série
sonarr_set_season_monitoring(series_id, season_number, monitored) write (Dé)monitore une saison précise
sonarr_search_season(series_id, season_number, confirm=False) write Lance la recherche d'une saison
sonarr_delete_season(series_id, season_number, confirm=False) destructive Supprime tous les fichiers d'une saison
sonarr_delete_episode_file(episode_file_id, confirm=False) destructive Supprime un fichier d'épisode
sonarr_delete_series(series_id, delete_files=False, confirm=False) destructive Supprime une série

Radarr

Tool Type Description
radarr_system_status read Statut et version de Radarr
radarr_list_movies read Liste des films suivis
radarr_lookup_movie(term) read Recherche un film (pour ajout)
radarr_quality_profiles read Profils de qualité disponibles
radarr_root_folders read Dossiers racine configurés
radarr_queue read File de téléchargement + diagnostic des items bloqués (voir Sonarr)
radarr_disk_space read Espace disque par volume, le plus plein en premier
radarr_health read Avertissements de santé de l'instance
radarr_history(limit=20, event_type=None) read Événements récents (grab/import/…) avec downloadId ; filtre event_type optionnel (voir ci-dessous)
radarr_delete_queue_item(queue_id=None, download_id=None, remove_from_client=True, blocklist=False, confirm=False) write Retire un item (par queue_id) ou tous ceux d'un même download_id — exactement un des deux
radarr_upcoming(days=7) read Films à venir via calendrier
radarr_add_movie(tmdb_id, quality_profile_id, root_folder_path, confirm=False) write Ajoute un film
radarr_set_movie_monitoring(movie_id, monitored) write (Dé)monitore un film
radarr_search_movie(movie_id, confirm=False) write Lance la recherche d'un film
radarr_delete_movie_file(movie_id, confirm=False) destructive Supprime le fichier d'un film (garde le film suivi)
radarr_delete_movie(movie_id, delete_files=False, confirm=False) destructive Supprime un film

qBittorrent (via qui)

Accès uniquement via qui (le gestionnaire web multi-instance d'autobrr), jamais via l'API qBittorrent directe. Auth par header X-API-Key. Les tools ciblent l'instance résolue depuis QUI_INSTANCE (id ou nom) ; si vide et qu'une seule instance existe, elle est choisie automatiquement ; si plusieurs, une erreur liste les instances disponibles.

Tool Type Description
qbit_list_instances read Instances qBittorrent gérées par qui (id + nom)
qbit_list_torrents(filter=None) read Torrents de l'instance (nom, hash complet, état, %, taille, ratio, catégorie) ; filter = recherche libre (matche aussi le hash)
qbit_get_torrent(hash) read Détail d'un torrent par hash ou préfixe unique (pont avec le downloadId Sonarr/Radarr, insensible à la casse)
qbit_pause(hash) control Met un torrent en pause (réversible, pas de confirm)
qbit_resume(hash) control Reprend un torrent (réversible, pas de confirm)
qbit_delete_torrent(hash, delete_files=False, confirm=False) destructive Retire un torrent de qBittorrent, avec option suppression des fichiers

Les tools prenant un hash acceptent le hash complet (40 car., copiable depuis qbit_list_torrents) ou un préfixe unique ; un préfixe ambigu liste les candidats sans agir.

Le hash qBittorrent est la clé de liaison : c'est la valeur renvoyée par le downloadId de l'historique Sonarr/Radarr. La comparaison est insensible à la casse (qBit renvoie le hash en minuscules, les *arr souvent en majuscules).

Prowlarr (indexeurs)

Gestionnaire d'indexeurs Servarr — API en /api/v1 (et non v3), auth X-Api-Key. Orienté diagnostic des indexeurs.

Tool Type Description
prowlarr_system_status read Version de Prowlarr
prowlarr_list_indexers read Indexeurs configurés (id, nom, activé ✓/✗, protocole, privacy, catégories, tags), triés par nom
prowlarr_indexer_status read Indexeurs en échec / désactivés temporairement (+ disabledTill, dates d'échec) ; sinon « all indexers healthy »
prowlarr_health read Avertissements globaux Prowlarr (type/source/message)
prowlarr_test_indexer(indexer_id) action Teste la connectivité d'un indexeur → PASS/FAIL + message (pas de confirm)
prowlarr_test_all_indexers action Teste tous les indexeurs → résumé pass/fail, échecs mis en avant
prowlarr_search(query, indexer_ids=None, categories=None, limit=20) read Recherche cross-indexeurs (tout contenu) triée par seeders ; affiche guid+indexerId pour le grab
prowlarr_grab(guid, indexer_id, confirm=False) acquisition Envoie une release au download client de Prowlarr (dry-run/confirm)

prowlarr_indexer_status ne porte pas de message textuel de raison (l'API /indexerstatus n'expose que indexerId + horodatages) : il croise la liste des indexeurs pour le nom et affiche la date de reprise (disabledTill). Pour le « pourquoi » global, voir prowlarr_health.

Recherche & grab (contenu hors-*arr : ebooks, manga, logiciels…)

prowlarr_search interroge tous les indexeurs et renvoie, par release, la référence de grab (guid + indexerId) à passer à prowlarr_grab. Les résultats sont triés par seeders décroissant (le limit de Prowlarr n'étant pas un vrai plafond, la coupe est faite côté client).

prowlarr_grab envoie la release au download client configuré dans Prowlarr (dry-run par défaut ; confirm=True pour exécuter). Aucune catégorie n'est passée par le MCP : le classement final dans qBittorrent (ebook / logiciel / autre) est décidé par les Mapped Categories du download client, à configurer dans l'UI Prowlarr (Settings → Download Clients). S'il n'y a aucun download client, le grab renvoie un message clair (à ajouter d'abord dans l'UI). La recherche/le grab avec catégorie explicite restent gérés côté Prowlarr, pas ici.

Tools coordonnés — purge « partout »

Suppriment, en un geste avec aperçu et confirm, les fichiers bibliothèque (Sonarr/Radarr) ET le(s) torrent(s) correspondants côté qBittorrent-via-qui, cross-seeds inclus.

Tool Type Description
sonarr_purge_season(series_id, season_number, delete_torrent_files=True, include_loose_matches=True, confirm=False) destructive Purge une saison partout (fichiers Sonarr + torrents + cross-seeds)
radarr_purge_movie(movie_id, delete_torrent_files=True, include_loose_matches=True, confirm=False) destructive Purge un film partout (fichier Radarr + torrents + cross-seeds)

Flux :

  1. Lister les fichiers concernés côté *arr (saison / film) → nombre + taille.
  2. Extraire les downloadId depuis l'historique *arr (/history/series, /history/movie) → ensemble des hash d'origine (dédupliqués ; un season pack partage un seul downloadId).
  3. Côté qui, pour chaque origine : résoudre le torrent, puis local-matches?strict=true → cross-seeds (siblings).
  4. Ensemble à supprimer = origines présentes ∪ siblings, dédupliqué par hash. include_loose_matches=False exclut les siblings match_type ∈ {name, release} (garde les matches content_path) et indique combien ont été exclus.
  5. Dry-run (confirm=False) : aperçu exhaustif des deux côtés, rien supprimé. confirm=True : suppression des fichiers *arr puis un seul bulk-action delete (avec deleteFiles selon delete_torrent_files) sur tous les hash ; rapport combiné.

Cas limites gérés (sans planter) : aucun downloadId (historique purgé → suppression biblio seule, torrents à gérer à la main) ; origine absente de qBit (ignorée, signalée) ; saison/film sans fichier (torrents traités quand même) ; cross-seed indispo (repli sur les origines seules).

Honnêteté sur l'espace disque : les tailles bibliothèque et torrents ne sont jamais additionnées — hardlinkées, ce sont généralement les mêmes octets. L'aperçu les montre séparément et rappelle que, comme on supprime les deux côtés (+ cross-seeds), l'espace de ce contenu sera cette fois réellement libéré (≈ la plus grande des deux tailles, pas la somme).

Pattern dry-run / confirm

Toutes les actions à effet de bord (add_*, delete_*, search_*) acceptent un paramètre confirm:

  • confirm=False (défaut) → aperçu sans exécution (dry-run)
  • confirm=True → exécution réelle

Note hardlink : les tools de suppression de fichiers (sonarr_delete_season, sonarr_delete_episode_file, radarr_delete_movie_file) retirent les fichiers côté Sonarr/Radarr uniquement. Si les fichiers sont en hardlink avec un client torrent, l'espace disque n'est pas libéré tant que le torrent n'est pas aussi supprimé côté client. L'aperçu dry-run le rappelle.

Filtre event_type de *_history

L'API attend un entier pour son query param eventType, donc le filtrage est fait côté client sur le champ texte eventType de chaque événement. event_type accepte :

Alias Correspond à (eventType canonique)
grabbed grabbed
imported downloadFolderImported
failed downloadFailed
deleted episodeFileDeleted (Sonarr) / movieFileDeleted (Radarr)
renamed episodeFileRenamed (Sonarr) / movieFileRenamed (Radarr)
ignored downloadIgnored

La chaîne canonique exacte est aussi acceptée (ex. event_type="downloadFolderImported"). Une valeur inconnue renvoie un message listant les valeurs valides, sans appel API. Comme le filtrage est côté client sur une fenêtre élargie (une requête, pageSize = max(limit*5, 100)), un résultat filtré partiel ajoute une note showing N of up to {limit} (searched the {window} most recent events).

Diagnostic & regroupement de *_queue

sonarr_queue / radarr_queue surfacent, pour chaque item, pourquoi il est bloqué : trackedDownloadStatus / trackedDownloadState (ex. warning / importBlocked), le texte des statusMessages et l'errorMessage éventuel. Les messages par item sont bornés ((+N more)) pour rester lisibles ; un champ absent/null est géré sans erreur.

Les items partageant le même downloadId (un season pack = un torrent, N lignes) sont regroupés en une entrée [×N] affichant le downloadId (le pont vers qBittorrent) et la ligne ids: … (les queue IDs individuels du groupe, tronquée si trop longue). Les items sans downloadId restent individuels et conservent taille/ETA.

*_delete_queue_item accepte exactement un de queue_id (un item) ou download_id (tous les items du download, retirés en un seul DELETE /queue/bulk) ; en dry-run il liste le nombre d'items, leur(s) titre(s) et les IDs ciblés avant toute suppression.

Développement

# Lint & format
uv run ruff check src tests
uv run ruff format src tests

# Tests
uv run pytest

Architecture

src/media_mcp/
  config.py          # pydantic-settings — lit les variables d'env
  models.py          # modèles pydantic pour les réponses simplifiées
  coordinated.py     # service d'orchestration purge (arr + qui), logique lourde
  server.py          # instancie FastMCP et enregistre tous les tools
  __main__.py        # entrypoint: python -m media_mcp
  clients/
    base.py          # ArrClient: httpx async, gestion des erreurs
    sonarr.py        # SonarrClient(ArrClient)
    radarr.py        # RadarrClient(ArrClient)
    prowlarr.py      # ProwlarrClient(ArrClient) — /api/v1
    qui.py           # QuiClient: httpx async, header X-API-Key (NE dérive PAS d'ArrClient)
  tools/
    sonarr_tools.py  # @mcp.tool pour Sonarr
    radarr_tools.py  # @mcp.tool pour Radarr
    qbit_tools.py    # @mcp.tool pour qBittorrent via qui
    prowlarr_tools.py     # @mcp.tool pour Prowlarr (indexeurs)
    coordinated_tools.py  # @mcp.tool purge saison/film "partout" (arr + qui)

Ajouter un nouveau service (ex. Jellyseerr) : créer clients/jellyseerr.py et tools/jellyseerr_tools.py, puis enregistrer dans server.py.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选