Synapse
June 10, 2026 ยท View on GitHub
Transform your Plex library and media stack into a conversational, interactive experience by integrating Plex Media Server, Radarr, Sonarr, and SABnzbd via a secure, self-hosted Discord agent.
Table of Contents
- Architecture Overview
- Configuration & Setup
- Discord Setup & Prerequisites
- User Interaction & Chat Flow Guide
- Deployment Options
- Interactive Discord Workflows
- Swapping the LLM Backend
- Verification & Troubleshooting
Architecture Overview
Synapse operates as a Go-native multi-agent service communicating with local MCP child processes via standard streams (stdin/stdout). This model avoids exposing network ports or API endpoints for MCP communication, maintaining a zero-trust footprint within your local network.
graph TD
User([User / Discord Requester]) -->|1. Request Media| Synapse[Synapse Agent]
Moderator([Synapse Moderator / Admin]) -->|3. Button Approve/Deny| DiscordUI[Discord UI Gateway]
Watchdog[Server Watchdog Agent] -->|Alerts & Imports| DiscordUI
Synapse -->|2. Submits Request| SQLite[(SQLite DB & Audit Logs)]
Synapse -.->|Checks Permission| DiscordUI
DiscordUI -->|4. Calls Tool| MCPManager[MCP Manager]
subgraph "MCP Servers (stdio standard streams)"
MCPManager --> PlexMCP[Plex MCP Server]
MCPManager --> RadarrMCP[Radarr MCP Server]
MCPManager --> SonarrMCP[Sonarr MCP Server]
MCPManager --> SABnzbdMCP[SABnzbd MCP Server]
end
PlexMCP --> PMS[Plex Media Server]
RadarrMCP --> Radarr[Radarr API]
SonarrMCP --> Sonarr[Sonarr API]
SABnzbdMCP --> SABnzbd[SABnzbd API]
Key Architectural Advantages
- Discord-as-a-First-Class-UI: Interactivity is handled via Discord rich embeds, buttons, and custom threads.
- Role-Based Access Control (RBAC): Integrates directly with Discord server roles.
- Cooperating Multi-Agent Team:
- Synapse Agent: The conversational interface. Automatically switches between direct execution (for admins/moderators) and routing requests through a database approval flow (for standard requesters).
- Server Watchdog: A background loop monitoring server load. Logs transcoding alerts, buffers, and concurrent streams, routing system warnings directly to the moderator's Discord audit channel. It also monitors import directories to broadcast public announcements when new media is ready.
- Local SQLite Persistence: Media requests, user preference memory, system events, and audit logs are persisted in a lightweight
synapse_agent.db.
Configuration & Setup
Both Docker container deployments and building/running from source read environment settings from a shared .env file in the project root.
- Clone the repository:
git clone https://github.com/jrudio/synapse.git cd synapse - Configure your
.envfile: Create a.envfile in the project directory using the template below:# Plex Configuration PLEX_BASE_URL=http://192.168.1.100:32400 PLEX_TOKEN=your_secure_plex_token_here # Radarr API Configuration RADARR_BASE_URL=http://192.168.1.154:7878 RADARR_API_KEY=your_api_key_here # Sonarr API Configuration SONARR_BASE_URL=http://192.168.1.156:8989 SONARR_API_KEY=your_api_key_here # SABnzbd API Configuration SABNZBD_BASE_URL=http://192.168.1.155:8080 SABNZBD_API_KEY=your_api_key_here # Discord Configuration DISCORD_TOKEN=your_discord_token_here DISCORD_REQUESTER_ROLE=synapse requester DISCORD_MODERATOR_ROLE=synapse moderator DISCORD_ADMIN_ROLE=synapse admin # Target Channel IDs for routing notifications DISCORD_APPROVAL_CHANNEL_ID=123456789012345678 DISCORD_AUDIT_LOG_CHANNEL_ID=123456789012345679 DISCORD_PUBLIC_CHANNEL_ID=123456789012345680 # SQLite Database Location DB_PATH=/app/data/synapse_agent.db # Context Window Chat History Limit (number of messages to retain in sliding window) SYNAPSE_HISTORY_LIMIT=20 # LLM Model Configuration (Defaults to Gemini 3.5 Flash) LLM_PROVIDER=gemini LLM_MODEL=gemini-3.5-flash LLM_API_KEY=your_gemini_api_key_here
Discord Setup & Prerequisites
Before starting Synapse, you must configure roles and channels in your Discord server and gather their IDs.
Step 1: Enable Discord Developer Mode
To copy the IDs of channels and roles, you must enable Developer Mode in your Discord client:
- Open Discord and go to User Settings (cog icon next to your username).
- Go to Advanced (under App Settings).
- Toggle on Developer Mode.
Step 2: Create Roles & Assign Permissions
Synapse maps bot permissions directly to your Discord server's role names. You can use the default role names or customize them.
Create the following three roles in your server settings (Server Settings > Roles):
| Role Level | Config Variable | Default Value | Suggested Setup & Capabilities |
|---|---|---|---|
| Admin | DISCORD_ADMIN_ROLE | synapse admin | Assign to server owners. Enables whitelist management (!whitelist) and reading flagged logs (!flagged). |
| Moderator | DISCORD_MODERATOR_ROLE | synapse moderator | Assign to trusted friends/family. Enables approving/denying media downloads, speed limits, and playback remote control. |
| Requester | DISCORD_REQUESTER_ROLE | synapse requester | Assign to all server members. Enables searching Plex library, saving rating preferences, and requesting new media downloads. |
Note
Role permissions are hierarchical. Admins inherit Moderator and Requester privileges; Moderators inherit Requester privileges.
Step 3: Setup Channels & Gather Channel IDs
Create four channels for the bot's operation. We suggest these names:
- Approval Channel (Suggested:
#synapse-requestsor#media-approvals)- Purpose: Where Synapse posts interactive buttons (Approve/Deny) for pending media requests.
- Access: Restricted to users with the Moderator or Admin roles.
- How to get ID: Right-click the channel name in Discord and click Copy Channel ID. Set as
DISCORD_APPROVAL_CHANNEL_ID.
- Audit Log Channel (Suggested:
#synapse-auditor#bot-audit-logs)- Purpose: Where Synapse posts system audit logs, watchdog health alerts, and transcode load warnings.
- Access: Restricted to administrators.
- How to get ID: Right-click the channel name and click Copy Channel ID. Set as
DISCORD_AUDIT_LOG_CHANNEL_ID.
- Public Announcement Channel (Suggested:
#synapse-new-mediaor#recently-added)- Purpose: Where the bot posts auto-announcements when a movie or TV show finishes downloading and is ready to stream.
- Access: Read-only for general server members, write access for the bot.
- How to get ID: Right-click the channel name and click Copy Channel ID. Set as
DISCORD_PUBLIC_CHANNEL_ID.
- Public Chat Channel (Suggested:
#synapse-chator#ask-synapse)- Purpose: The main public channel where server members can chat with Synapse, search Plex libraries, ask for recommendations, and request downloads.
- Access: Open to all members with the Requester role.
User Interaction & Chat Flow Guide
To interact with Synapse, users must understand the chat rules and how the sliding context window functions.
Chat Scopes & Context Window Limit
Every channel or direct message (DM) session has its own isolated context memory.
- Conversations in
#synapse-chatdo not bleed into DMs or other channels. - Context Limit: To keep response times fast and prevent LLM confusion, Synapse maintains a sliding window of the last 20 messages per channel/session (configurable via
SYNAPSE_HISTORY_LIMITin.env). - Tool Call Safety: If the 20-message boundary cuts off in the middle of a database query or tool result, the pruning algorithm automatically retains the matching function call to prevent model errors.
Interaction Rules (Mention/Reply/Thread)
When a user mentions (tags) the bot in a public channel, Synapse will automatically start a new thread (named "Synapse Chat - username") off that message and post its reply in the thread. Users can then continue chatting in that thread without needing to tag the bot again.
| Chat Context | Trigger Rule | Requires @Synapse or Reply? | Suggested Use Case |
|---|---|---|---|
| Direct Message (DM) | Auto-responds to all messages. | No | Private requests, ratings, and catalog searching. |
| Discord Thread | Auto-responds to all messages inside a thread started by the bot. | No | Complex curations or recommendations discussions. |
| Public Channels | Auto-spawns a new thread on @Synapse mention, or responds if replying to a bot message. | Yes (Initially) | General searches, whitelisting commands, or starting a discussion. |
Tip
Ignoring messages without mentions in public channels prevents the bot from spamming or interrupting general human-to-human discussion in shared channels, and stops infinite loops if multiple bots are present in the same room.
Deployment Options
Option A: Docker Deployment (Recommended)
- Configure your
docker-compose.yml: Ensure yourdocker-compose.ymlmounts a local directory for persistence and maps all environmental keys:version: '3.8' services: synapse-agents: image: jrudio/synapse-agents:latest container_name: synapse-agents restart: unless-stopped stdin_open: true # Allows interactive terminal chat with Synapse tty: true # Enables TTY for CLI interactive launcher volumes: - ./data:/app/data environment: - PLEX_BASE_URL=${PLEX_BASE_URL} - PLEX_TOKEN=${PLEX_TOKEN} - LLM_MODEL=${LLM_MODEL} - GEMINI_MODEL=${GEMINI_MODEL} - LLM_API_KEY=${LLM_API_KEY} - GEMINI_API_KEY=${GEMINI_API_KEY} - LLM_API_BASE_URL=${LLM_API_BASE_URL} - GEMINI_API_BASE_URL=${GEMINI_API_BASE_URL} - LLM_PROVIDER=${LLM_PROVIDER} - RADARR_BASE_URL=${RADARR_BASE_URL} - RADARR_API_KEY=${RADARR_API_KEY} - SONARR_BASE_URL=${SONARR_BASE_URL} - SONARR_API_KEY=${SONARR_API_KEY} - SABNZBD_BASE_URL=${SABNZBD_BASE_URL} - SABNZBD_API_KEY=${SABNZBD_API_KEY} - DISCORD_TOKEN=${DISCORD_TOKEN} - DISCORD_REQUESTER_ROLE=${DISCORD_REQUESTER_ROLE} - DISCORD_MODERATOR_ROLE=${DISCORD_MODERATOR_ROLE} - DISCORD_ADMIN_ROLE=${DISCORD_ADMIN_ROLE} - DISCORD_APPROVAL_CHANNEL_ID=${DISCORD_APPROVAL_CHANNEL_ID} - DISCORD_AUDIT_LOG_CHANNEL_ID=${DISCORD_AUDIT_LOG_CHANNEL_ID} - DISCORD_PUBLIC_CHANNEL_ID=${DISCORD_PUBLIC_CHANNEL_ID} - DB_PATH=${DB_PATH} - SYNAPSE_HISTORY_LIMIT=${SYNAPSE_HISTORY_LIMIT} - Start the stack:
docker-compose up -d
Option B: Build from Source
- Clone and compile the binaries:
go build -o synapse-mcp-go ./main.go go build -o radarr-mcp ./cmd/radarr-mcp go build -o sonarr-mcp ./cmd/sonarr-mcp go build -o sabnzbd-mcp ./cmd/sabnzbd-mcp go build -o synapse-agents ./cmd/agents - Setup your
mcp_config.json:{ "mcpServers": { "plex": { "command": "./synapse-mcp-go" }, "radarr": { "command": "./radarr-mcp" }, "sonarr": { "command": "./sonarr-mcp" }, "sabnzbd": { "command": "./sabnzbd-mcp" } } } - Run the agent launcher:
./synapse-agents
Interactive Discord Workflows
1. Media Discovery & Curation (Requester / Moderator / Admin)
Standard users can query Synapse for media recommendations or search library contents:
User: "What Sci-Fi movies do we have on Plex?"
Synapse: "We have Inception (2010) and Interstellar (2014). Would you like to check details on either of them?"
2. Interactive Request Flow (Requester)
If a standard requester asks to add a movie or show, Synapse intercepts the command and routes it to the database for moderator review:
User: "Can you add the movie Severance?"
Synapse: (Searches TMDB and identifies ID) "I have submitted a request for Severance (2025). It is pending moderator approval."
Moderator Review (Approval Channel)
An embed is posted in the configured moderator channel with interactive buttons:
๐ฅ Media Download Request
Title: Severance
Type: MOVIE
Year: 2025
Requester: @User
Request ID: 42
[ Approve ] [ Deny ]
- If a moderator clicks [ Approve ], Synapse sends a command to the Radarr MCP sub-process to add the movie, removes the buttons, turns the embed border green, and updates status to:
โ Approved by @Moderator. - The bot announces in the public channel:
"๐ Request Approved: **Severance** (2025) has been added to the download queue by a moderator!"
3. Server Watchdog Alerts (Moderator / Admin Channel)
If a user triggers a transcode load, the Watchdog identifies it during periodic polling and notifies the audit channel:
๐ Audit Log Entry
User: @system (server_watchdog)
Action: high_load_alert
Details: High CPU usage warning: 1 active video transcoding stream(s) detected. The CPU is experiencing heavy load transcoding codecs for clients.
Swapping the LLM Backend
Synapse includes a translation adapter translating ADK payloads to standard OpenAI /v1/chat/completions specifications, enabling compatibility with local or cloud engines.
Configuration Environment Variables
Configure these variables in your .env file to select and customize your model:
| Environment Variable | Description | Allowed Values / Defaults |
|---|---|---|
LLM_PROVIDER | The backend LLM provider to use. | gemini (default), openai, ollama |
LLM_MODEL | The model name to run. | Defaults: gemini-3.5-flash (Gemini), gpt-4o (OpenAI), llama3 (Ollama) |
LLM_API_BASE_URL | The endpoint URL of the model provider. | Defaults: Google API (Gemini), https://api.openai.com/v1 (OpenAI), http://localhost:11434/v1 (Ollama) |
LLM_API_KEY | The API Key/Token of the provider. | None (Ollama requires none by default) |
Setup Examples
1. Ollama Setup (Self-Hosted)
LLM_PROVIDER=ollama
LLM_MODEL=gemma4:9b # Set your pulled Ollama model name
LLM_API_BASE_URL=http://localhost:11434/v1 # Address of Ollama API
2. ChatGPT Setup
LLM_PROVIDER=openai
LLM_MODEL=o3-mini # Target model
LLM_API_KEY=your_api_key_here
3. Claude Setup (LiteLLM/OpenRouter)
LLM_PROVIDER=openai # We use the OpenAI translation protocol
LLM_MODEL=anthropic/claude-3-7-sonnet
LLM_API_BASE_URL=http://localhost:4000/v1 # Your local LiteLLM / OpenRouter endpoint
LLM_API_KEY=your_api_key_here
Credits & Libraries
- go-plex-client: Synapse uses the
go-plex-clientlibrary to interact with the Plex Media Server and Plex.tv APIs.