Skip to main content
Glama
swe-sanad

jdownloader-mcp

by swe-sanad

jdownloader-mcp

An MCP server that lets an agent drive a local JDownloader 2 through its entire download lifecycle — add links / containers, watch the linkgrabber resolve them, move them to the queue, set priority, force-start, track progress, inspect download/extract directories, and clean up.

It exists for the one download workload the shell can't already do: premium hosters, one-click hosters, .dlc/.crypt containers, and captcha-gated folder links. JDownloader stays the moat for that case — but plain video/social URLs (TikTok, YouTube, X, Instagram, Reddit, ...) are often where JD's plugins fall behind (anti-bot walls, "plugin outdated"). For those, the server also carries a yt-dlp backend and a smart_download router that tries JDownloader first and auto-falls-back to yt-dlp when a link breaks, so the agent can finish the download either way instead of stopping at "left to the shell."

Thin wrapper over myjdapi and yt-dlp; built on the mcp 1.x FastMCP API.

Status: v0.6.0 — 62 tools. monitor() is the tool for watching a queue over time: a live snapshot for polling — overall state + speed, download counts bucketed by category, active-download progress, and blocked hosts, in one call. The full v0.3 tool surface has now been exercised live against a real JDownloader 2: 24 safe tools (config, cleanup/update, variants/organize, extensions, dialogs, basic-auth/account creds reads, plus the original 34 v0.2.0 lifecycle/query/account tools) are live-verified, on top of 91+ mocked unit tests and the full smoke_test.py lifecycle. That live pass found and fixed two bugs: get_download_urls (now returns each link's url via query_links) and get_variants (now handles non-variant links gracefully) — both are now live-verified working, as is set_variant. Destructive tools (clear_linkgrabber, update_jd, restart_jd, reconnect) remain unit-tested only by design — not exercised live.

Lifecycle

add_links / add_container ─▶ LINKGRABBER ─(move_to_downloads)─▶ DOWNLOADS ─▶ extract ─▶ remove
                            list_linkgrabber                    list_downloads / force_start / wait_for

Item IDs (uuid) come from the list_* tools; most tools take package_ids / link_ids plus a stage (linkgrabber | downloads).

See Agent lifecycle below for worked tool-call flows, and the full tool reference for every parameter.

Related MCP server: revula

Tools (62)

Every tool returns {"ok": true, "result": ...} on success or {"ok": false, "error": "..."} on failure — no tool raises.

Lifecycle (16)

Tool

Parameters

Description

add_links

urls: list[str], package: str | None, destination_folder: str | None, priority: str = "DEFAULT", extract_password: str | None, download_password: str | None, autostart: bool = False

Add URLs to the linkgrabber. autostart=True moves them straight to the download queue once resolved. priority: HIGHEST..LOWEST.

add_container

container_type: str, content: str

Add a link container (container_type: DLC | RSDF | CCF) by its content.

list_linkgrabber

List linkgrabber packages+links (online/offline status) and whether JD is still collecting. Use the uuid fields to act on items.

list_downloads

List download-queue packages with progress_pct, speed, ETA, status, and save path (saveTo). The workhorse for polling.

status

Overall dashboard: state, running/paused, aggregate speed (bytes/s), premium, speed-limit state, bytes downloaded, and update_available.

wait_for

package_id: str, timeout_s: int = 60

Bounded poll of one download package (cap 120s). Returns finished / failed / stalled / still_running / not_found — finished/failed/stalled are terminal, re-call only on still_running.

move_to_downloads

package_ids: list[str] | None, link_ids: list[str] | None

Promote resolved linkgrabber items to the download queue.

control_downloads

action: str

Global queue control. action: start | stop | pause | resume.

force_start

package_ids: list[str] | None, link_ids: list[str] | None

Force the given items to download now, bypassing queue/connection limits.

set_priority

priority: str, stage: str = "linkgrabber", package_ids: list[str] | None, link_ids: list[str] | None

Set priority (HIGHEST..LOWEST) on items in the given stage (linkgrabber | downloads).

set_enabled

enabled: bool, stage: str = "downloads", package_ids: list[str] | None, link_ids: list[str] | None

Enable/disable items in the given stage (linkgrabber | downloads).

get_directories

package_ids: list[str] | None

Per-package save paths + the default download folder + extraction settings (extension enabled, custom extract path).

set_download_dir

directory: str, package_ids: list[str]

Set the download directory for the given download-queue packages.

set_extraction

enabled: bool | None, custom_path: str | None

Toggle the extraction extension and/or set a custom extract path.

remove

stage: str = "downloads", package_ids: list[str] | None, link_ids: list[str] | None, from_disk: bool = False

Remove items from a list. from_disk=True (downloads only) also deletes the files from disk.

reset

package_ids: list[str] | None, link_ids: list[str] | None

Reset download-queue items so they re-download from scratch.

Ergonomic / composite (3)

Tool

Parameters

Description

download

urls: list[str], dest: str | None, package: str | None, priority: str = "DEFAULT"

One-shot download: add URLs, auto-move to the queue, start, and return the resulting download package. The common case in a single call.

check_availability

urls: list[str]

Check whether URLs are online (and their size) WITHOUT queueing them. Resolves in the linkgrabber, reports per-link availability, then cleans up.

find

query: str

Find packages by name substring across both lists -> uuid + stage.

Accounts / premium hosters (6)

Tool

Parameters

Description

list_accounts

List premium/hoster accounts: hostname, username, valid, traffic left, expiry (-1 = unlimited/none), enabled, uuid.

list_premium_hosters

filter: str | None, limit: int = 100

List hosters JDownloader supports (optionally filtered by substring).

add_account

hoster: str, username: str, password: str

Add a premium/hoster account. The password is a hoster credential supplied by the user; it travels through the My.JDownloader relay (end-to-end encrypted) to your JDownloader instance and is never stored by this server.

remove_accounts

account_ids: list[str]

Remove hoster accounts by uuid. Irreversible — re-adding requires the credentials again.

set_account_enabled

enabled: bool, account_ids: list[str]

Enable or disable hoster accounts by uuid. Disabling can stop in-flight premium downloads that rely on the account.

refresh_accounts

account_ids: list[str] | None

Refresh hoster account status/traffic (all accounts if none given).

System / control (5)

Tool

Parameters

Description

get_storage_info

Free/total disk per drive (with free_gb and used_pct) — check before a large download.

restart_jd

Restart the JDownloader application (recovery).

get_speed_limit

Current download speed-limit state: enabled + limit in bytes/s.

set_speed_limit

enabled: bool, bytes_per_sec: int | None

Enable/disable the download speed limit and optionally set it (bytes/s).

reconnect

Trigger a router reconnect to get a new IP (resets some hoster limits).

Captcha — human-in-the-loop (3)

Tool

Parameters

Description

list_captchas

List captchas JDownloader is currently waiting on.

get_captcha

captcha_id: str

Fetch a pending captcha (image data) for a human to solve.

answer_captcha

captcha_id: str, solution: str

Submit a human-provided solution for a pending captcha.

Config (generic settings) (3)

Tool

Parameters

Description

get_config

interface: str, key: str

Read one JDownloader config value. interface is a settings interface name (e.g. org.jdownloader.settings.GeneralSettings); find keys with list_config.

list_config

filter: str | None, limit: int = 200

List JD config entries (interface/key/value), optionally filtered by substring. JD exposes ~2200 keys — always pass a filter and expect total to exceed limit.

set_config

interface: str, key: str, value: str | int | bool

Write one JDownloader config value; returns the re-read value. Use list_config/get_config first to confirm the exact interface + key.

Cleanup, update & diagnostics (8)

Tool

Parameters

Description

clear_linkgrabber

backup: bool = True

Clear the ENTIRE linkgrabber list. With backup=True (default) it first saves every link URL to a backup file (returned as backup_file) so the wipe is recoverable via restore_linkgrabber.

restore_linkgrabber

backup_file: str

Re-add links from a backup file written by clear_linkgrabber.

clean_downloads

what: str = "finished"

Remove a category of links from the download list (files stay on disk): what = finished | offline | failed | disabled | all. Use offline to purge dead "File not found" links clogging a stuck queue.

diagnose_queue

One-shot health summary of the download queue: totals, unfinished links bucketed by status and host, and pending captcha/dialog counts. The fast way to understand WHY a queue is stuck without dumping every link.

recommend

Turn the current queue state into prioritized, actionable fixes (offline links, anti-bot/plugin hosts, waiting captchas, stopped-with-pending, low disk, bad accounts). Priority 1 = most urgent. The "what should I do?" tool.

monitor

Live monitoring snapshot for polling: overall state + speed, download counts bucketed by category (downloading | pending | done | stopped | error | blocked), the currently-active downloads with progress/speed/eta, and which hosts are blocked.

wait_for_change

since: str | None = None, timeout_s: int = 600, poll_s: int = 15

Block (server-side) until the queue status changes, then return the new monitor snapshot — or {changed: false, timed_out: true} after timeout_s. The MCP polls internally, so you spend no tokens while waiting and only get woken on a real change. Pass since = the last signature you saw; omit to baseline on the current state. Call again after a timeout to keep waiting.

update_jd

Apply an available JDownloader update and restart it. Disruptive — downloads pause during the restart.

Variants & organize (5)

Tool

Parameters

Description

get_variants

link_id: str

List the variants (audio/video/quality) of a multi-variant link.

set_variant

link_id: str, variant_id: str

Select a variant (from get_variants) for a multi-variant link.

rename

new_name: str, package_id: str | None, link_id: str | None

Rename a linkgrabber package (package_id) or link (link_id).

move_to_new_package

new_name: str, package_ids: list[str] | None, link_ids: list[str] | None, dest: str | None, stage: str = "linkgrabber"

Move the given items into a new package named new_name in the given stage (linkgrabber | downloads).

get_download_urls

package_ids: list[str] | None, link_ids: list[str] | None

Get the resolved original download URLs for the given linkgrabber items.

Extensions (2)

Tool

Parameters

Description

list_extensions

List JDownloader extensions (id, name, installed, enabled).

set_extension_enabled

extension_id: str, enabled: bool

Enable or disable a JDownloader extension by id (from list_extensions).

Dialogs (3)

Tool

Parameters

Description

list_dialogs

List interactive dialogs JDownloader is currently waiting on.

get_dialog

dialog_id: str

Fetch a pending dialog's details/options.

answer_dialog

dialog_id: str, data: dict

Answer a pending dialog with a data payload (shape per the dialog type).

Basic-auth & account credentials (5)

Tool

Parameters

Description

list_basic_auth

List stored FTP/HTTP basic-auth entries.

add_basic_auth

auth_type: str, hostmask: str, username: str, password: str

Add an FTP/HTTP basic-auth entry (auth_type: FTP | HTTP). The password is a user-supplied credential; it travels the My.JDownloader relay (end-to-end encrypted) and is never stored by this server.

remove_basic_auth

auth_ids: list[str]

Remove FTP/HTTP basic-auth entries by id.

set_account_credentials

account_id: str, username: str, password: str

Update a hoster account's username/password. The password is a user-supplied credential; it travels the My.JDownloader relay (end-to-end encrypted) and is never stored by this server.

get_premium_hoster_url

hoster: str

Get the signup/premium URL for a hoster.

Escape hatch (1)

Tool

Parameters

Description

raw_action

path: str, params: list | None, http_action: str = "POST"

Call any JD API endpoint not wrapped above (e.g. premium accounts, captcha, reconnect, speed-limit). path like /downloadsV2/setPriority; params is the positional argument list that endpoint expects.

yt-dlp backend (2)

Tool

Parameters

Description

ytdlp_download

url: str, dest: str | None, audio_only: bool = False, cookies_from_browser: str | None, cookies_file: str | None

Download a URL directly via yt-dlp (thousands of video/social sites); bypasses JDownloader. For private/login-gated content pass cookies_from_browser (chrome | firefox | edge | ... — browser must be CLOSED on Windows) or cookies_file (a Netscape cookies.txt path — works while the browser is open).

smart_download

url: str, dest: str | None, audio_only: bool = False, backend: str | None, cookies_from_browser: str | None, cookies_file: str | None, jd_wait_s: int = 45

Known video/social sites (tiktok, youtube, x, instagram, reddit, ...) go straight to yt-dlp. Everything else is tried on JDownloader first; if the link breaks within jd_wait_s (offline / error / plugin-outdated / stalled) it auto-falls-back to yt-dlp. Override with backend='ytdlp'|'jd' to force one engine (strict, no fallback).

16 + 3 + 6 + 5 + 3 + 3 + 8 + 5 + 2 + 3 + 5 + 1 + 2 = 62 tools.

Agent lifecycle

JDownloader keeps two separate lists. Understanding this is the key to using the tools correctly:

  1. Linkgrabber — a staging area. add_links / add_container drop URLs here; JD resolves them (checks online/offline, size, host) in the background. Inspect with list_linkgrabber.

  2. Downloads queue — the actual download engine. Nothing downloads until an item is here. Promote resolved linkgrabber items with move_to_downloads, or add with autostart=True / use the download tool to skip straight there. Inspect and poll with list_downloads / status / wait_for.

uuid values returned by any list_* tool are the IDs (package_ids / link_ids) that every other tool expects — there is no other way to get an ID.

Config surface: JD exposes its full ~2,200-key settings tree via get_config / list_config / set_config. Don't try to enumerate it — start with list_config(filter=...) to find the interface/key you need, then get_config/set_config on that exact pair. List cleanup: once you're done with a package, clear_linkgrabber wipes the whole staging list — by default it backs up every link URL first (backup=True), so the wipe is recoverable via restore_linkgrabber(backup_file) — and clean_downloads (what = finished | offline | failed | disabled | all) removes a category of entries from the download queue (files stay on disk) — both are separate from the per-item remove tool above. Stuck queue? Call diagnose_queue first — it buckets unfinished links by status/host and surfaces pending captcha/dialog counts in one call, before you reach for list_downloads/list_captchas individually. Then call recommend() for the quickest path to an actionable next step — it turns that same queue state (offline links, anti-bot/plugin hosts, waiting captchas, stopped-with-pending, low disk, bad accounts) into a prioritized fix list. Watching a queue over time? monitor gives the same status/host buckets plus per-download progress/speed/eta for active items — it's the one to poll on an interval instead of re-calling diagnose_queue/list_downloads separately. Token-free waiting: instead of calling monitor() on a timer, call wait_for_change (optionally with since = the last snapshot's signature) — it blocks server-side and only returns when the queue actually changes or the timeout elapses, so you don't burn tokens on unchanged snapshots.

stateDiagram-v2
    [*] --> Linkgrabber: add_links / add_container
    Linkgrabber --> Linkgrabber: list_linkgrabber while resolving
    Linkgrabber --> Downloads: move_to_downloads
    Linkgrabber --> [*]: remove to discard
    Downloads --> Downloads: control_downloads / force_start / set_priority
    Downloads --> Downloads: list_downloads / status / wait_for to poll
    Downloads --> Finished: wait_for returns finished
    Downloads --> Failed: wait_for returns failed or stalled
    Finished --> [*]: remove / get_directories
    Failed --> Downloads: reset to retry
    Failed --> [*]: remove

Polling pattern: list_downloads / status are the workhorses — call them as often as you like. wait_for is a bounded convenience (hard-capped at 120s server-side); for anything longer, loop: call wait_for, and if it returns still_running, call it again. A stalled result means the item is idle with no progress (captcha / reconnect needed / all links offline) — don't re-poll blindly, investigate first (e.g. list_captchas).

"Just download this" (fire and forget)

1. download(urls=[...])              # add + auto-queue + start in one call
2. wait_for(package_id)              # poll until finished/failed/stalled
3. get_directories(package_ids)      # confirm where the files landed

For a fast preflight check without committing to a download, use check_availability(urls) first — it reports online/offline + size and leaves nothing queued (timed_out flags if the check itself ran out of time).

"Careful lifecycle" (inspect before committing)

1. add_links(urls, package="my-set")
2. list_linkgrabber()                # confirm links resolved online, get uuid
3. move_to_downloads(package_ids=[uuid])
4. force_start(package_ids=[uuid])   # or control_downloads("start")
5. wait_for(package_id=uuid)
6. remove(stage="downloads", package_ids=[uuid])   # cleanup once finished

"Manage premium accounts"

1. list_accounts()                          # see current hosters, validity, traffic
2. add_account(hoster, username, password)  # add a new one, or:
   set_account_enabled(enabled, account_ids) # temporarily disable one, or:
   refresh_accounts(account_ids)             # re-check traffic/validity

"Precondition checks" (before a large or unattended download)

1. get_storage_info()   # confirm free_gb on the target drive
2. list_accounts()      # confirm the needed hoster account is valid/enabled
3. download(urls, dest=...)

Downloading video / social (yt-dlp)

smart_download(url) is JDownloader-first with an auto-fallback to yt-dlp. Known video/social hosts (tiktok, youtube, x/twitter, instagram, facebook, reddit, vimeo, twitch, soundcloud, ...) route straight to ytdlp_download. Everything else is tried on JDownloader's download first; if that link breaks within jd_wait_s seconds (default 45 — offline / error / plugin-outdated / stalled), the failed JD entry is auto-removed and the URL falls back to ytdlp_download. The result carries "routed_to": "jd" when JD is handling it, or "routed_to": "jd->ytdlp" plus jd_outcome/jd_reason when a fallback happened. Override with backend='ytdlp'|'jd' to force one engine (strict, no fallback), or call ytdlp_download directly to skip the router.

Private/login-gated content (your own TikTok videos, members-only posts) needs cookies — pick one:

  • cookies_from_browser='chrome'|'firefox'|'edge'|... — reads that browser's cookie store directly. On Windows the browser must be closed first (it locks the cookie DB while running).

  • cookies_file='path/to/cookies.txt' — a Netscape-format cookies file exported via a "Get cookies.txt" browser extension. Works while the browser stays open.

ffmpeg must be on PATH for audio_only=True (audio extraction) and for merging separate video+audio streams on formats that need it — yt-dlp will otherwise fail or fall back to a lower-quality single-stream format.

Connection modes

Set via MYJD_CONNECT_MODE in .env (default relay):

  • relay (default) — connects via My.JDownloader, which works through NAT with no port-forwarding. myjdapi automatically upgrades data calls to a direct LAN connection when JD advertises one, so only the initial handshake actually touches the cloud. Uses MYJD_EMAIL / MYJD_PASSWORD / MYJD_DEVICE.

  • direct — pure-local via JDownloader's built-in RemoteAPI, no cloud account involved. Set MYJD_CONNECT_MODE=direct plus MYJD_DIRECT_IP (default 127.0.0.1) / MYJD_DIRECT_PORT (default 3128). Requires enabling RemoteAPI in JDownloader first (Settings → Advanced Settings, search "RemoteAPI" / "Deprecated"). It's a deprecated, more limited API that may not support every tool here — relay is the full-featured default; only use direct if you specifically need to avoid the cloud account.

Setup

  1. In JDownloader: pick a connection mode (see above). For the default relay mode, go to Settings → My.JDownloader, sign in / link the device, note its device name. For direct mode, instead enable RemoteAPI (Settings → Advanced Settings, search "RemoteAPI").

  2. Configure creds:

    cp .env.example .env   # then edit .env
  3. Install deps (already done if you used uv):

    uv sync

    Deps: mcp, myjdapi, requests, yt-dlp. For the yt-dlp backend, also put ffmpeg on PATH (needed for audio_only extraction and video+audio merges) — JDownloader-only usage doesn't need it.

Verify

uv run pytest                     # mocked unit tests (no JD needed)
uv run python smoke_test.py       # live end-to-end against your JD2 (uses .env)

The smoke test walks the whole lifecycle with a tiny file and cleans up. It SKIPs cleanly if the env vars aren't set.

Register with Claude Code

claude mcp add jdownloader -- uv run --directory E:/SWE-Pioneers/projects/jdownloader-mcp jdownloader-mcp

or add to your MCP client config (stdio):

{
  "mcpServers": {
    "jdownloader": {
      "command": "uv",
      "args": ["run", "--directory", "E:/SWE-Pioneers/projects/jdownloader-mcp", "jdownloader-mcp"]
    }
  }
}

The server reads MYJD_CONNECT_MODE plus either the relay creds (MYJD_EMAIL / MYJD_PASSWORD / MYJD_DEVICE) or the direct-mode settings (MYJD_DIRECT_IP / MYJD_DIRECT_PORT) from .env (or the environment). See Connection modes.

Docs

  • docs/2026-08-01-jdownloader-mcp-design.md — design spec.

  • docs/myjdapi-api-reference.md — exact installed-API reference the code targets.

License

MIT — see LICENSE.

Install Server
A
license - permissive license
A
quality
A
maintenance

Maintenance

Maintainers
Response time
0dRelease cycle
9Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • An MCP server that let you interact with Cycloid.io Internal Development Portal and Platform

  • MCP server for the FFmpeg Micro video transcoding API — create, monitor, download transcodes.

  • A basic MCP server to operate on the Postman API.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/swe-sanad/jdownloader-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server