Skip to main content
Glama

plex-mcp-server

A Model Context Protocol server for Plex Media Server.

Two layers in one server:

  1. Every documented Plex Media Server API operation as an MCP tool. The server ships an OpenAPI 3.1 document of the Plex API, generates one tool per operation from it, and re-generates the document from Plex's published documentation on demand. Nothing is hand-waved: if Plex documents it, there is a tool for it.

  2. Curated tools that do what a caller would otherwise have to reconstruct — resolve a title to a rating key, read Plex's filter vocabulary, compute per-user statistics, recommend something to watch, control a client, write a spoiler-free recap of a film in progress.

plex_library_list            plex_sessions_list           plex_media_search
plex_media_edit              plex_playlist_create         plex_collection_create
plex_analytics_overview      plex_recommendations         plex_clients_control
plex_subtitles_dialogue      plex_watchlist_add           plex_stream_url
plex_api_request             plex_endpoint_search         plex_library_section_get_all
…and one tool per documented operation

Why this exists

There are several Plex MCP servers (see Credits). Each one is good at something different, and each one stops short of the full API. This project merges the useful parts, adds complete endpoint coverage generated from Plex's own OpenAPI document, and ships the specification so other tools can consume it.

Related MCP server: Plex Assistant MCP

Quick start

You need a Plex server address and a token.

git clone https://github.com/aminamos/plex-mcp-server.git
cd plex-mcp-server
npm install
npm run build

Get a Plex token

  1. Sign in to Plex in a browser.

  2. Open any library item, then Get Info → View XML.

  3. Copy the X-Plex-Token query parameter from the URL that opens.

Or, if you only have an account token, leave PLEX_URL unset: the server discovers your owned servers through plex.tv/api/v2/resources and picks the best connection.

Wire it into an MCP client

{
  "mcpServers": {
    "plex": {
      "command": "node",
      "args": ["/absolute/path/to/plex-mcp-server/dist/index.js"],
      "env": {
        "PLEX_URL": "http://192.168.1.10:32400",
        "PLEX_TOKEN": "your-token"
      }
    }
  }
}

A .env file in the working directory (or ~/.config/plex-mcp-server/.env) is also read, so you can keep credentials out of client configs:

PLEX_URL=http://192.168.1.10:32400
PLEX_TOKEN=xxxxxxxxxxxx

HTTP transport

node dist/index.js --transport http --host 127.0.0.1 --port 3000 --path /mcp

Streamable HTTP, with optional access control:

Mode

Variables

None (default, and the default bind is 127.0.0.1)

Static bearer token

MCP_AUTH_TOKEN

OAuth 2.1 with an external authorization server (RS256, verified against the issuer's JWKS)

MCP_OAUTH_ISSUER, MCP_OAUTH_AUDIENCE

With OAuth configured, the server publishes /.well-known/oauth-protected-resource and proxied /.well-known/oauth-authorization-server metadata, and answers unauthenticated requests with a WWW-Authenticate challenge — the flow remote MCP clients expect.

Configuration

Flags override environment variables, which override .env.

Flag

Environment

Default

Meaning

--plex-url

PLEX_URL

discovered from plex.tv

Plex Media Server base URL

--plex-token

PLEX_TOKEN

X-Plex-Token for the server

--cloud-token

PLEX_CLOUD_TOKEN

falls back to PLEX_TOKEN

plex.tv account token (account, watchlist, Discover)

--client-identifier

PLEX_CLIENT_IDENTIFIER

generated once, then cached

Stable X-Plex-Client-Identifier

--transport

MCP_TRANSPORT

stdio

stdio or http

--host / --port / --path

MCP_HTTP_HOST / MCP_HTTP_PORT / MCP_HTTP_PATH

127.0.0.1 / 3000 / /mcp

HTTP bind

--tools

PLEX_TOOL_MODE

all

all = one tool per documented operation; curated = only the hand-written tools

--tags

PLEX_TOOL_TAGS

Restrict tools to these OpenAPI tags, e.g. Library,Status

--include / --exclude

PLEX_TOOL_INCLUDE / PLEX_TOOL_EXCLUDE

Filter tools by name substring

--read-only

PLEX_READ_ONLY

off

Do not register or allow anything that is not GET

--format

PLEX_OUTPUT_FORMAT

auto

json, compact (tabular), or auto (shortest)

PLEX_MAX_ITEMS

250

Cap on items returned from one list endpoint

PLEX_MAX_DESCRIPTION_LENGTH

1200

Cap on generated tool descriptions

PLEX_REQUEST_TIMEOUT_MS

30000

HTTP timeout

PLEX_CACHE_TTL_MS

0

In-memory GET cache; 0 disables

OPENSUBTITLES_API_KEY

Enables the subtitle tools

MCP_LOG_LEVEL

info

debug, info, warn, error (stderr only)

PLEX_SERVERS / PLEX_SERVERS_TOKENS

Extra servers for cross-server search

A note on tool count

Full coverage means a lot of tools: --tools all advertises one tool per documented operation, and a tools/list response of that size costs real context (order of tens of thousands of tokens, depending on the document version). Options:

  • --tools curated — a few dozen hand-written tools, smallest footprint.

  • --tags Library,Status — keep the generated coverage but only for the areas you care about.

  • Keep all and let the client search: every generated tool is named after its operation, and plex_endpoint_search finds operations by keyword without loading the whole list.

--read-only is enforced at registration and again at call time: mutating tools — generated and curated — are not registered at all, and the escape hatch (plex_api_request) refuses non-GET methods. A read-only server therefore never advertises a tool that cannot run.

What is in the box

Generated tools. One per operation of the bundled OpenAPI document, named plex_ + the snake_cased operationId, e.g. GET /library/sections/{sectionId}/allplex_library_section_get_all. Parameters (path and query — Plex documents nearly everything as query parameters) become the tool's JSON schema, and the tool performs exactly that request. See docs/ENDPOINTS.md for the full list.

Where a curated tool and a generated tool would share a name, the curated one wins and the generated one is not registered — that is deliberate for the three cases where the curated version does strictly more work (resolving a title to a Discover GUID for the watchlist, cross-checking Discover results against the local library). Use plex_api_request to reach the raw endpoint form of those operations.

Curated tools. See docs/TOOLS.md for the generated catalogue. The areas they cover:

Area

Examples

Library

list sections, per-section statistics, recently added, scan / refresh / analyze / empty trash, filter and sort vocabulary

Media

cross-library search, full detail, children (seasons, episodes), metadata edit with lock semantics, artwork, watch state, ratings

Playlists & collections

create from rating keys, add/remove items, edit, copy to a user

Sessions

live streams, watch history, terminate a stream, transcodes

Server

info, bandwidth, host and process resources, Butler tasks, preferences, clean bundles, database optimisation

Clients

inventory and timelines, playback control (play / pause / stop / seek / step / skip), stream selection

Users

shared and home users, per-user on-deck, watch history, activity statistics

Analytics

play/user/popular/library reporting over any window, computed in-process

Recommendations

scored suggestions from a user's actual watch history, with reasons

Watchlist & Discover

the account watchlist, and Discover search with "already in my library" cross-checks

Subtitles

find subtitles, and extract dialogue up to a playback position

Streaming

direct-play URLs, transcode decisions, subtitle streams

Discovery tools.

  • plex_endpoint_search — find an operation by keyword; returns the tool name, method, path and required arguments.

  • plex_api_request — arbitrary request to the server, for endpoints, parameters or iterators that are not in the document.

  • plex_api_info — what the bundled document contains and how tools are named.

Resources. plex://openapi/index (the whole endpoint catalogue in one read), plex://openapi/tag/<tag>, plex://openapi/document (the raw OpenAPI document), plex://registry (every tool with tag, access class and schema), and plex://server/info (the live server).

Prompts. library_report, server_health, and what_have_i_missed — the last one reads the active session, pulls the subtitles for what is playing, and asks for a recap that stops at the current playback position, so it cannot spoil the rest of the film.

The OpenAPI document

spec/plex-pms.openapi.json (and .yaml) is generated, not hand-maintained:

npm run spec:build      # fetch developer.plex.tv/pms, extract, normalise, merge extensions
npm run spec:validate   # structural + policy checks

Plex publishes the Plex Media Server API as documentation rendered by ReDoc from an OpenAPI 3.1 document embedded in the page. scripts/build-spec.mjs extracts that document, normalises it (title, summary, server variables, provenance), and merges spec/extensions.yaml.

spec/extensions.yaml is hand-authored and holds endpoints in wide use that Plex's published document omits — /library/sections, /library/recentlyAdded, /library/metadata/{ids}/children, /statistics/bandwidth, /clients, /diagnostics/*, the Plex Cloud account endpoints and the Discover watchlist. Each one is marked x-plex-extension: true, and each carries the MCP-specific annotations this project defines:

Extension

Meaning

x-mcp-tool

Explicit MCP tool name for the operation

x-mcp-host

pms (default), cloud (plex.tv) or discover (discover.provider.plex.tv)

x-mcp-tag

Override the tag used for PLEX_TOOL_TAGS filtering

x-mcp-skip

Do not generate a tool for this operation

x-mcp-notes

Extra guidance appended to the generated tool description

Provenance — source URL, extraction timestamp, upstream version, operation counts — is recorded in spec/plex-pms.openapi.json under x-plex-provenance.

Licensing of the document

The upstream document is published by Plex under Apache 2.0; that licence and attribution are preserved in info.license and x-plex-provenance. spec/extensions.yaml and everything under src/ are this project's own work, under MIT.

Development

npm run build      # tsc → dist/
npm test           # vitest
npm run smoke      # end-to-end: real server process, mock Plex, real MCP round trip
npm run spec:build # regenerate the OpenAPI document
npm run docs       # regenerate docs/TOOLS.md and docs/ENDPOINTS.md from the running server
npm run check      # build + validate the spec + tests

The smoke test is the honest one: it boots dist/index.js as a subprocess, points it at a mock Plex server, and drives initialize / tools/list / tools/call / resources/read / prompts/get over stdio, asserting both the MCP surface and the HTTP requests the server makes.

Limitations

  • Subtitle tools need an OpenSubtitles API key; without it they fail with a clear message and the rest of the server is unaffected.

  • plex_clients_* tools talk to the client devices directly, so they need the client's host and port to be reachable from wherever this server runs.

  • Plex's documented response format is XML by default; this server always asks for JSON, and reports the raw body as text when an endpoint answers with something that is not JSON (transcode playlists, image endpoints).

  • Generated tools return Plex's payloads trimmed to PLEX_MAX_ITEMS with an explicit truncation marker. Curated tools return shaped results.

  • The bundled document is a snapshot. Re-run npm run spec:build to pick up Plex's changes.

Credits

This server merges the useful parts of four existing projects. No code was copied — the implementations here are new — but the scope, the tool taxonomy and several features come directly from them:

  • vladimir-tutin/plex-mcp-server — the broad tool taxonomy (library, media, playlist, collection, user, sessions, server, client), the remote-access story with OAuth against an external issuer, and administrative tooling (logs, bandwidth, Butler, empty trash, optimise database, clean bundles).

  • niavasha/plex-mcp-server — the architecture: one unified binary, tools separated from their schemas and implementation, tool annotations, write-operation gating, a compact/TOON-style tabular encoding that is only used when it is actually shorter, and viewing analytics built from Plex's own endpoints.

  • eddmann/plex-mcp — the idea worth stealing: ground answers in subtitles up to the current playback position, so a recap of a film in progress cannot spoil it. That is plex_subtitles_dialogue and the what_have_i_missed prompt.

  • BenjaminOddou/alfred-plex — search and filter ergonomics (reading Plex's filter/sort vocabulary and exposing it to a caller), Discover search, server actions (settings, statistics, history), the watchlist toggle, deep links into Plex Web, and streaming URLs handed to a player.

Endpoint coverage comes from Plex's own documentation at https://developer.plex.tv/pms/.

License

MIT — see LICENSE. The generated OpenAPI document retains Plex's Apache 2.0 attribution; the details are in NOTICE and inside the document's info.license and x-plex-provenance fields.

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    C
    maintenance
    Enables search, browsing, and management of Plex media libraries including movies, TV shows, and music, with support for playlists, watch history, natural language music discovery, and detailed media information retrieval.
    22
    23 npm
    5
    ISC
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables users to manage and control their Plex media library through natural language commands in MCP-compatible AI clients. It supports searching content, managing playlists, tracking library statistics, and monitoring live viewing sessions.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Lets AI assistants browse your libraries, search media, get viewing recommendations, check what's on deck, and more — all read-only against your local Plex instance.
    110 npm
    1
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables full management of a Plex Media Server via Claude, including browsing libraries, fixing metadata, managing collections, and more.
    -