Skip to main content
Glama

sf-mm-mcp

An MCP server that exposes moviemagnet.site (movies) and showfeed.site (TV shows) to LLM clients, with put.io as the download backend.

Search a title, find magnet links ranked by seeders, and start the download on put.io — all from inside Claude Desktop, Claude Code, opencode, or any other MCP client.

npx sf-mm-mcp login    # one-time put.io sign-in
npx sf-mm-mcp          # start the server on stdio
npx sf-mm-mcp http     # ...or on streamable HTTP, for clients that connect by URL

Setup

1. Add the server to your MCP client

The put.io app id (9525) is built in, so there is nothing to configure for a default install.

Claude Desktop (claude_desktop_config.json), Claude Code (.mcp.json), and opencode (opencode.json) all use the same shape:

{
  "mcpServers": {
    "sf-mm": {
      "command": "npx",
      "args": ["-y", "sf-mm-mcp"]
    }
  }
}

opencode

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "sfmm": {
      "type": "local",
      "command": ["npx", "-y", "sf-mm-mcp"],
      "enabled": true
    }
  }
}

opencode prefixes tool names with the server name, so the tools appear as sfmm_movies_search, sfmm_putio_login, and so on. Verify the connection with opencode mcp list.

Note that opencode mcp auth does not apply here. That command drives OAuth for remote MCP servers via dynamic client registration; this is a local stdio server that manages its own put.io credential. Use the sign-in below instead.

See docs/clients.md for Cursor, VS Code, Windsurf, Zed, Cline and others.

2. Sign in

Either run npx sf-mm-mcp login in a terminal, or ask your assistant to call the putio_login tool and follow the instructions it returns.

putio_login is non-blocking by design, so the in-chat flow works: the tool returns a code immediately, you enter it at put.io/link, and putio_auth_status confirms. The token is stored per-machine, so signing in once covers every MCP client on that machine.

Related MCP server: Arr Suite MCP Server

Authentication

The default is put.io's OOB (out-of-band) device flow: the server requests a short code, you enter it at https://put.io/link, and the server polls until it is linked.

This is the default rather than the more familiar redirect flow for a specific reason: an npx-distributed package cannot hold a client secret. Anything baked into the tarball is readable by everyone who installs it. The OOB flow needs no secret and no loopback listener, so it also works over SSH and inside containers.

If you control the environment and prefer the redirect flow, set:

SF_MM_AUTH_FLOW=loopback
PUTIO_CLIENT_SECRET=...          # supply it yourself; never commit it
PUTIO_REDIRECT_URI=http://127.0.0.1:41830/callback

The redirect URI must match one registered on your put.io app. state is generated per handshake and verified on the callback.

Where the token is stored

In order of preference:

  1. PUTIO_TOKEN environment variable, if set (nothing is persisted).

  2. OS keychain — macOS Keychain via security, Linux via secret-tool.

  3. A 0600 file at ~/.config/sf-mm-mcp/token.json.

npx sf-mm-mcp status reports which backend is in use. npx sf-mm-mcp logout clears it.

Tools

Discovery — no sign-in required

Tool

Purpose

movies_search

Find movies by title, returns IMDb ids

movies_get

Full details for one movie

movies_popular

Most-viewed movies on moviemagnet.site

shows_search

Find TV shows by title

shows_get

Show details plus episode list, optionally one season

shows_popular

Most-viewed shows on showfeed.site

Torrents — sign-in required

Tool

Purpose

movies_find_torrents

Magnets for a movie, filterable by resolution and seeders

shows_find_torrents

Magnets for a show, filterable by season, episode, resolution

Results are normalised to {title, magnet, sizeHuman, seeders, resolution, season, episode, ageDays} and sorted by seeder count.

put.io — sign-in required

Tool

Purpose

putio_add_transfer

Start a download from a magnet or URL

putio_list_transfers

List transfers with progress

putio_get_transfer

Poll a single transfer

putio_cancel_transfers

Cancel or remove transfers

putio_clean_transfers

Clear completed transfer entries

putio_account

Username and disk usage

putio_list_files

Browse files and folders

favorites_list / favorites_update

Read and edit the favorites list

Favorites are stored in the put.io account config under the same favorites key the two websites use, so the list stays in sync across all three.

Auth

putio_login, putio_auth_status, putio_logout.

putio_login returns instructions immediately rather than blocking — waiting for a human to finish a browser flow would hold the tool call open for minutes and most clients time out first. Poll putio_auth_status to confirm.

Configuration

All optional.

Variable

Default

Purpose

PUTIO_CLIENT_ID

9525

put.io OAuth app id (public); override to use your own app

PUTIO_TOKEN

—

Use a token directly, skipping sign-in

SF_MM_AUTH_FLOW

oob

oob or loopback

PUTIO_CLIENT_SECRET

—

Only for loopback

PUTIO_REDIRECT_URI

http://127.0.0.1:41830/callback

Only for loopback

MOVIEMAGNET_BASE_URL

https://moviemagnet.site

Point at a dev instance

SHOWFEED_BASE_URL

https://showfeed.site

Point at a dev instance

PUTIO_API_BASE_URL

https://api.put.io/v2

SF_MM_CONFIRM_TRANSFERS

false

Require confirm=true on putio_add_transfer

SF_MM_TORRENT_CACHE_TTL

900

Torrent result cache, seconds

SF_MM_METADATA_CACHE_TTL

3600

Metadata cache, seconds

SF_MM_TOKEN_STORE

auto

auto/keychain/file/none; none disables persistence

SF_MM_LOG_LEVEL

info

debug/info/warn/error/silent

SF_MM_TRANSPORT

stdio

stdio or http (same as the http subcommand)

SF_MM_HTTP_HOST

127.0.0.1

HTTP mode bind address

SF_MM_HTTP_PORT

8009

HTTP mode port

SF_MM_HTTP_PATH

/mcp

HTTP mode endpoint path

SF_MM_HTTP_TOKEN

—

Require Authorization: Bearer <token> in HTTP mode

Transports

stdio (default) is what desktop MCP clients spawn. Streamable HTTP is for clients that connect by URL and cannot spawn a process — Open WebUI, for example:

SF_MM_HTTP_TOKEN=$(openssl rand -hex 32) npx -y sf-mm-mcp http
# → http://127.0.0.1:8009/mcp

GET /health is available for supervisors and returns the running version.

HTTP mode shares one put.io account across every client that can reach the port. Unlike stdio, where the server belongs to a single desktop user, there is no per-user authorization: anyone who can call the endpoint can spend the linked account's storage and bandwidth. It therefore binds to loopback by default and refuses nothing else — bind wider only together with SF_MM_HTTP_TOKEN, and consider SF_MM_CONFIRM_TRANSFERS=true.

See docs/clients.md for the Open WebUI setup and a systemd unit.

Caching and rate limits

Both websites validate the put.io token on every /api/torrent request, and that endpoint fans out to torrent indexers. An LLM will call these far more eagerly than a human clicking through a web page, so this server:

  • caches torrent results (15 min) and metadata (1 hour) in process,

  • de-duplicates concurrent identical requests,

  • caches token-validation results for 10 minutes rather than revalidating per call,

  • honours put.io's 429 plus X-RateLimit-Reset headers with backoff,

  • never retries a write, so a transfer cannot be silently created twice.

Development

npm install
npm run build
npm run typecheck
npm test           # boots the server, asserts tool surface; no network
npm run smoke      # exercises the live public endpoints over MCP stdio

npm test is network-free on purpose, so CI does not go red because a torrent indexer is having a bad day. It runs with SF_MM_TOKEN_STORE=none so it cannot accidentally pick up a developer's real token and skip the auth-gating checks.

Releasing

npm version patch && git push --follow-tags

Publishing runs from GitHub Actions on a version tag. There is no npm token anywhere: the workflow authenticates with npm via OIDC trusted publishing, which also generates a provenance attestation automatically.

npm version patch     # or minor / major - commits and tags
git push --follow-tags

.github/workflows/publish.yml then refuses to continue unless:

  • the tag matches package.json version exactly,

  • that version is not already on npm,

  • typecheck, build and the tool-surface test all pass,

  • no .env file made it into the tarball.

To rehearse without publishing, run the workflow manually from the Actions tab with dry-run left checked. Full detail in docs/releasing.md.

Documentation

docs/architecture.md

Design decisions and why the alternatives lost

docs/auth.md

Credential model; why there is no client secret

docs/upstream-apis.md

The two sites' API surface and its quirks

docs/clients.md

Configuration for every common MCP client

docs/releasing.md

Release procedure and CI guards

Agents should start at AGENTS.md.

Point at local dev servers with MOVIEMAGNET_BASE_URL=http://localhost:5173.

Notes on the upstream APIs

Neither site publishes a schema, so the shapes in src/schemas.ts were derived by reading the SvelteKit route handlers and probing the live endpoints. Two things worth knowing:

  • Both sites expect the put.io token in a bare Authorization header with no Bearer prefix. This client matches that.

  • Neither site has a "create transfer" endpoint — the web apps do it client-side. So transfers, account info, and favorites go directly to api.put.io.

Validation failures are logged rather than swallowed, so upstream schema drift shows up as a warning on stderr instead of a silent "no results found".

A bug this client used to work around

Both sites once returned 500 on the first request for any given title, because torrentService.js destructured the Jackett response as const { instance, data: results } — declaring a new block-scoped results that shadowed the outer one, leaving it undefined on every cache miss. It hid well: the shadowed value was still written to Redis, so a retry hit the cache and succeeded. Titles with zero indexer results were never cached and so failed permanently.

Fixed and deployed in both apps (showfeed-app@6640310, moviemagnet-app@77b22fe), along with queryMultipleServices, which only ever called resolve and therefore hung forever if every indexer failed.

The 5xx retry in this client predates those fixes. It is kept because indexers still fail transiently, but it is no longer masking a deterministic upstream failure. Full write-up in docs/upstream-apis.md.

Credits

Logo icons from pepicons by CyCraft, licensed CC BY 4.0. See assets/README.md.

Available Tools

20 tools
favorites_listList favoritesA
Read-only

Lists the IMDb ids favorited in the put.io account config. This is the same list shown on showfeed.site and moviemagnet.site.

Inputs: no named parameters.

favorites_updateAdd or remove a favoriteA
Idempotent

Adds or removes an IMDb id from the favorites list stored in the put.io account config.

Inputs: action (required), imdbId (required).

movies_find_torrentsFind movie torrentsA
Read-only

Searches torrent indexers for a movie by IMDb id and returns magnet links sorted by seeder count. Requires a put.io sign-in. Pass a magnet to putio_add_transfer to start downloading.

Inputs: imdbId (required), limit, minSeeders, resolutions, +1 more.

movies_getGet movie detailsA
Read-only

Fetches full details for a single movie by IMDb id from moviemagnet.site. No sign-in required.

Inputs: imdbId (required).

putio_accountGet put.io account infoA
Read-only

Returns the signed-in put.io username and remaining disk space.

Inputs: no named parameters.

putio_add_transferAdd a transfer to put.ioA

Starts a download on put.io from a magnet link or HTTP URL, typically one returned by movies_find_torrents or shows_find_torrents. This spends the account's bandwidth and storage, so confirm the choice with the user first.

Inputs: url (required), confirm, saveParentId.

putio_auth_statusCheck put.io sign-in statusA
Read-only

Reports whether a valid put.io token is stored, and the progress of any sign-in started by putio_login.

Inputs: no named parameters.

putio_cancel_transfersCancel put.io transfersA
DestructiveIdempotent

Cancels or removes transfers by id. Stops seeding if the transfer is seeding, otherwise removes the transfer entry. Downloaded files are not deleted.

Inputs: transferIds (required).

putio_clean_transfersClear completed put.io transfersA
DestructiveIdempotent

Removes all completed transfer entries from the transfer list. Downloaded files are not deleted.

Inputs: no named parameters.

putio_get_transferGet a put.io transferA
Read-only

Fetches up-to-date details for a single transfer, useful for checking download progress.

Inputs: transferId (required).

putio_list_filesList put.io filesA
Read-only

Lists files and folders in the put.io account. Pass a folder id to browse into it.

Inputs: parentId.

putio_list_transfersList put.io transfersA
Read-only

Lists current and recent transfers on the signed-in put.io account, with download progress.

Inputs: limit, status.

putio_loginSign in to put.ioA

Starts the put.io sign-in flow and returns instructions for the user to complete it in a browser. Call putio_auth_status afterwards to confirm completion. Required before searching torrents or creating transfers.

Inputs: force.

putio_logoutSign out of put.ioA
Destructive

Deletes the stored put.io token from the OS keychain or credential file.

Inputs: no named parameters.

shows_find_torrentsFind TV show torrentsA
Read-only

Searches torrent indexers for a show by IMDb id and returns magnet links sorted by seeder count. Filter to a specific season and episode to find a single airing. Requires a put.io sign-in.

Inputs: imdbId (required), limit, season, episode, +3 more.

shows_getGet TV show detailsA
Read-only

Fetches details for a single show by IMDb id, including its full episode list. No sign-in required.

Inputs: imdbId (required), season, includeEpisodes.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.9/5.0

    Scored across 20 tools

    Disambiguation4/5

    Tools are cleanly namespaced by domain (movies_, shows_, putio_, favorites_) and each targets a distinct resource or action. The only potential source of confusion is putio_clean_transfers versus putio_cancel_transfers, but their descriptions clearly separate bulk cleanup of completed entries from targeted cancellation/removal.

    Naming Consistency4/5

    Most tools follow a consistent [resource]_[action] pattern, with actions like get, search, add, list, cancel, update. A few outliers use adjectives or nouns for the suffix (movies_popular, putio_account, putio_auth_status), which breaks the verb_noun convention, but the overall prefix scheme remains predictable.

    Tool Count3/5

    With 20 tools, the server is at the heavy end of the scale, but the count is justified by the multiple subdomains (movies, shows, put.io, favorites). It borders on feeling bloated, yet each tool serves a distinct function and the logical grouping helps manageability.

    Completeness4/5

    The tool surface covers the core lifecycle for discovering media, finding torrents, managing put.io transfers, and maintaining favorites. Notable minor gaps include no direct file deletion or transfer pause/resume, but these are workarounds.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers