nahojnet/plex-mcp-server
If you are the rightful owner of plex-mcp-server and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
Plex MCP Server is a production-ready HTTP server that integrates with the Plex Media Server API to provide read-only access to your Plex library using the Model Context Protocol (MCP).
Plex MCP Server
A production-ready Model Context Protocol (MCP) HTTP server with Plex Media Server API integration. This server provides read-only access to your Plex library through the standardized MCP protocol with HTTP streaming support.
Features
- MCP Protocol Compliant: Implements the Model Context Protocol specification (2024-11-05) with HTTP streaming
- Plex API Integration: Read-only access to 22 Plex API endpoints
- Secure Authentication: Bearer token authentication (supports both "Bearer token" and "token" formats)
- Docker Support: Fully containerized with Docker and Docker Compose
- Production Ready: Includes security best practices, logging, health checks, and monitoring
- Session Management: Automatic session handling with configurable timeouts
- SSE Streaming: Server-Sent Events support for real-time communication
Table of Contents
- Quick Start
- Installation
- Configuration
- API Documentation
- Security
- Troubleshooting
- Development
- License
Quick Start
- Clone the repository and navigate to the directory
- Copy
.env.exampleto.envand configure your settings:cp .env.example .env - Edit
.envwith your credentials:MCP_AUTH_TOKEN=your-secure-token-here PLEX_URL=http://localhost:32400 PLEX_TOKEN=your-plex-token-here - Start with Docker Compose:
docker-compose up -d - Test the server:
curl -H "Authorization: Bearer your-secure-token-here" \ http://localhost:3000/health
Installation
Using Docker Compose (Recommended)
This is the easiest method for deployment.
-
Create
.envfile:cp .env.example .env -
Configure environment variables in
.env:# Required MCP_AUTH_TOKEN=your-secure-random-token PLEX_URL=http://your-plex-server:32400 PLEX_TOKEN=your-plex-authentication-token # Optional PORT=3000 LOG_LEVEL=info -
Start the server:
docker-compose up -d -
View logs:
docker-compose logs -f -
Stop the server:
docker-compose down
Using Docker
If you prefer to use Docker directly without Compose:
-
Build the image:
docker build -t plex-mcp-server . -
Run the container:
docker run -d \ --name plex-mcp-server \ -p 3000:3000 \ -e MCP_AUTH_TOKEN="your-token" \ -e PLEX_URL="http://plex-server:32400" \ -e PLEX_TOKEN="your-plex-token" \ --restart unless-stopped \ plex-mcp-server
Manual Installation
For local development or non-Docker deployments:
-
Prerequisites:
- Node.js 20.x or higher
- npm or yarn
-
Install dependencies:
npm install -
Configure environment:
cp .env.example .env # Edit .env with your settings -
Build the project:
npm run build -
Start the server:
npm startOr for development with auto-reload:
npm run dev
Configuration
Environment Variables
All configuration is done through environment variables:
Required Variables
| Variable | Description | Example |
|---|---|---|
MCP_AUTH_TOKEN | Authentication token for MCP server | a3f8d9c2b1e4... |
PLEX_URL | URL to your Plex Media Server | http://localhost:32400 |
PLEX_TOKEN | Plex authentication token | xyz123abc456... |
Optional Variables
| Variable | Description | Default |
|---|---|---|
PORT | Server listening port | 3000 |
HOST | Server binding address | 127.0.0.1 (Docker: 0.0.0.0) |
MCP_ENDPOINT | MCP endpoint path | /mcp |
ALLOWED_ORIGINS | CORS allowed origins (comma-separated) | http://localhost:*,http://127.0.0.1:* |
LOG_LEVEL | Logging level (error, warn, info, debug) | info |
Getting Your Plex Token
To find your Plex authentication token:
- Sign in to your Plex Web App
- Open any media item
- Click "Get Info" or press
i - Click "View XML"
- Look for
X-Plex-Tokenin the URL
Or follow the official Plex guide.
Generating a Secure Auth Token
Generate a secure random token for MCP_AUTH_TOKEN:
# Using OpenSSL
openssl rand -hex 32
# Using Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Using Python
python3 -c "import secrets; print(secrets.token_hex(32))"
API Documentation
MCP Protocol
The server implements the Model Context Protocol over HTTP with the following endpoints:
POST /mcp- Send JSON-RPC requestsGET /mcp- Open SSE stream (requires session)DELETE /mcp- Terminate sessionGET /health- Health check (no auth required)
Authentication
Include your token in the Authorization header:
# With "Bearer" prefix
Authorization: Bearer your-token-here
# Without "Bearer" prefix (also supported)
Authorization: your-token-here
MCP Methods
Initialize Session
{
"jsonrpc": "2.0",
"method": "initialize",
"params": {},
"id": 1
}
List Available Tools
{
"jsonrpc": "2.0",
"method": "tools/list",
"params": {},
"id": 2
}
Call a Tool
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "plex_get_server_info",
"arguments": {}
},
"id": 3
}
Available Tools
The server provides 22 read-only tools for accessing Plex data:
| Tool Name | Description | Required Arguments |
|---|---|---|
plex_get_server_info | Get server information and capabilities | None |
plex_get_identity | Get server identity | None |
plex_get_all_preferences | Get all server preferences | None |
plex_get_preference | Get a specific preference | id |
plex_get_library_sections | Get all library sections | None |
plex_get_library_section | Get a specific library section | sectionId |
plex_get_library_section_items | Get items in a library section | sectionId |
plex_get_metadata | Get metadata for a specific item | ratingKey |
plex_get_metadata_children | Get children of a metadata item | ratingKey |
plex_get_activities | Get current server activities | None |
plex_get_butler_tasks | Get Butler maintenance tasks | None |
plex_get_global_hubs | Get global content hubs | None |
plex_get_continue_watching | Get continue watching items | None |
plex_search_library | Search across libraries | query |
plex_get_sessions | Get active playback sessions | None |
plex_get_statistics | Get media statistics | None |
plex_get_playlists | Get all playlists | None |
plex_get_playlist_items | Get items in a playlist | playlistId |
plex_get_recently_added | Get recently added items | None |
plex_get_on_deck | Get on deck items | None |
plex_get_collections | Get collections in a section | sectionId |
plex_search | Search all libraries | query |
Usage Examples
Example 1: Get Server Information
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-token" \
-H "Accept: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "plex_get_server_info",
"arguments": {}
},
"id": 1
}'
Example 2: Search Library
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: your-token" \
-H "Accept: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "plex_search",
"arguments": {
"query": "Inception",
"limit": 10
}
},
"id": 2
}'
Example 3: Get Recently Added
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-token" \
-H "Accept: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "plex_get_recently_added",
"arguments": {}
},
"id": 3
}'
Example 4: Using SSE Streaming
# First, initialize a session
RESPONSE=$(curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-token" \
-H "Accept: text/event-stream" \
-d '{"jsonrpc": "2.0", "method": "initialize", "id": 1}' \
-D -)
# Extract session ID from response headers
SESSION_ID=$(echo "$RESPONSE" | grep -i "mcp-session-id" | cut -d' ' -f2 | tr -d '\r')
# Open SSE stream
curl -N http://localhost:3000/mcp \
-H "Authorization: Bearer your-token" \
-H "Mcp-Session-Id: $SESSION_ID"
Security
Security Features
This server implements multiple security layers:
- Authentication: Bearer token authentication with constant-time comparison
- Origin Validation: CORS with configurable allowed origins to prevent DNS rebinding
- Non-Root User: Docker container runs as non-privileged user (UID 1001)
- Read-Only Filesystem: Container uses read-only root filesystem
- Resource Limits: CPU and memory limits in Docker Compose
- Security Headers: Helmet.js for security HTTP headers
- Input Validation: Request validation and sanitization
- Session Management: Automatic session expiration (24 hours)
- Rate Limiting: Recommended to add reverse proxy with rate limiting
Security Best Practices
- Use Strong Tokens: Generate cryptographically secure random tokens
- HTTPS in Production: Always use HTTPS in production environments
- Firewall: Restrict access to the server port
- Reverse Proxy: Use nginx/Traefik with rate limiting and SSL termination
- Regular Updates: Keep dependencies and base images updated
- Monitor Logs: Review logs for unauthorized access attempts
- Network Isolation: Run in isolated Docker network when possible
Recommended Nginx Configuration
upstream plex_mcp {
server localhost:3000;
}
server {
listen 443 ssl http2;
server_name mcp.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# Rate limiting
limit_req_zone $binary_remote_addr zone=mcp_limit:10m rate=10r/s;
limit_req zone=mcp_limit burst=20 nodelay;
location /mcp {
proxy_pass http://plex_mcp;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# SSE support
proxy_buffering off;
proxy_read_timeout 86400;
}
}
Troubleshooting
Common Issues
Connection Refused
Problem: Cannot connect to the server
Solutions:
- Check if the container is running:
docker-compose ps - Verify port mapping:
docker-compose port plex-mcp-server 3000 - Check firewall rules
- Ensure
HOSTis set to0.0.0.0in Docker
Authentication Failed
Problem: 401 Unauthorized error
Solutions:
- Verify
MCP_AUTH_TOKENmatches in client and server - Check
Authorizationheader format - Ensure token doesn't contain extra whitespace
Plex Connection Error
Problem: Cannot connect to Plex server
Solutions:
- Verify
PLEX_URLis correct and accessible - Check
PLEX_TOKENis valid - Ensure Plex server is running
- Check network connectivity between containers
- Verify firewall rules for Plex port
Session Expired
Problem: 404 Session not found
Solutions:
- Re-initialize session
- Check session timeout settings (24 hours default)
- Verify
Mcp-Session-Idheader is being sent
Debugging
Enable debug logging:
# In .env
LOG_LEVEL=debug
# Or in docker-compose.yml
environment:
- LOG_LEVEL=debug
View detailed logs:
# Docker Compose
docker-compose logs -f --tail=100
# Docker
docker logs -f plex-mcp-server
Health Check
Check server health:
curl http://localhost:3000/health
Expected response:
{
"status": "ok",
"timestamp": "2024-01-01T12:00:00.000Z"
}
Development
Project Structure
plex-mcp-server/
├── src/
│ ├── index.ts # Application entry point
│ ├── server.ts # MCP HTTP server implementation
│ ├── auth.ts # Authentication middleware
│ ├── config.ts # Configuration management
│ ├── logger.ts # Logging configuration
│ ├── plex-client.ts # Plex API client
│ └── tools.ts # MCP tools definitions
├── Dockerfile # Docker image definition
├── docker-compose.yml # Docker Compose configuration
├── package.json # Node.js dependencies
├── tsconfig.json # TypeScript configuration
├── .env.example # Environment variables template
└── README.md # This file
Development Setup
-
Clone and install:
git clone <repository-url> cd plex-mcp-server npm install -
Configure environment:
cp .env.example .env # Edit .env with your settings -
Run in development mode:
npm run dev -
Build:
npm run build -
Lint:
npm run lint
Testing with MCP Inspector
You can test the server using the MCP Inspector tool:
npx @modelcontextprotocol/inspector http://localhost:3000/mcp
Adding New Tools
To add a new Plex API endpoint:
-
Add method to
PlexClient(src/plex-client.ts):async getNewEndpoint(param: string): Promise<any> { const response = await this.client.get('/new/endpoint', { params: { param } }); return response.data; } -
Add tool definition (
src/tools.ts):{ name: 'plex_get_new_endpoint', description: 'Description of the endpoint', inputSchema: { type: 'object', properties: { param: { type: 'string', description: 'Parameter description' } }, required: ['param'] } } -
Add tool handler (
src/tools.ts):case 'plex_get_new_endpoint': return await plexClient.getNewEndpoint(args.param);
Architecture
Technology Stack
- Runtime: Node.js 20.x (Alpine Linux)
- Language: TypeScript 5.x
- Framework: Express.js
- HTTP Client: Axios
- Logging: Winston
- Security: Helmet.js
- Container: Docker with multi-stage builds
MCP Protocol Flow
Client MCP Server Plex Server
| | |
|-- POST /mcp (initialize) ------>| |
|<-- Mcp-Session-Id --------------| |
| | |
|-- POST /mcp (tools/list) ------>| |
|<-- Available tools -------------| |
| | |
|-- POST /mcp (tools/call) ------>| |
| |-- GET /api/endpoint -------->|
| |<-- Plex data ----------------|
|<-- Tool result -----------------| |
| | |
|-- GET /mcp (SSE stream) ------->| |
|<-- SSE events (ongoing) --------| |
Performance
Resource Usage
- Memory: ~50-100MB idle, ~200MB under load
- CPU: Minimal (<5% on modern hardware)
- Network: Depends on Plex API response sizes
Scaling
For high-traffic scenarios:
- Deploy multiple instances behind a load balancer
- Use Redis for shared session storage
- Enable connection pooling for Plex API
- Implement response caching
License
MIT License - See LICENSE file for details
Support
For issues, questions, or contributions:
- Open an issue on GitHub
- Check existing issues for solutions
- Review logs for error details
Acknowledgments
- Model Context Protocol - Protocol specification
- Plex Media Server - Media server platform
- LukeHagar/plex-api-spec - OpenAPI specification
Note: This server provides read-only access to Plex. No data modification, deletion, or server configuration changes are possible through this MCP server.