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

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

  1. Discord-as-a-First-Class-UI: Interactivity is handled via Discord rich embeds, buttons, and custom threads.
  2. Role-Based Access Control (RBAC): Integrates directly with Discord server roles.
  3. 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.
  4. 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.

  1. Clone the repository:
    git clone https://github.com/jrudio/synapse.git
    cd synapse
    
  2. Configure your .env file: Create a .env file 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:

  1. Open Discord and go to User Settings (cog icon next to your username).
  2. Go to Advanced (under App Settings).
  3. 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 LevelConfig VariableDefault ValueSuggested Setup & Capabilities
AdminDISCORD_ADMIN_ROLEsynapse adminAssign to server owners. Enables whitelist management (!whitelist) and reading flagged logs (!flagged).
ModeratorDISCORD_MODERATOR_ROLEsynapse moderatorAssign to trusted friends/family. Enables approving/denying media downloads, speed limits, and playback remote control.
RequesterDISCORD_REQUESTER_ROLEsynapse requesterAssign 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:

  1. Approval Channel (Suggested: #synapse-requests or #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.
  2. Audit Log Channel (Suggested: #synapse-audit or #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.
  3. Public Announcement Channel (Suggested: #synapse-new-media or #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.
  4. Public Chat Channel (Suggested: #synapse-chat or #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-chat do 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_LIMIT in .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 ContextTrigger RuleRequires @Synapse or Reply?Suggested Use Case
Direct Message (DM)Auto-responds to all messages.NoPrivate requests, ratings, and catalog searching.
Discord ThreadAuto-responds to all messages inside a thread started by the bot.NoComplex curations or recommendations discussions.
Public ChannelsAuto-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

  1. Configure your docker-compose.yml: Ensure your docker-compose.yml mounts 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}
    
  2. Start the stack:
    docker-compose up -d
    

Option B: Build from Source

  1. 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
    
  2. Setup your mcp_config.json:
    {
      "mcpServers": {
        "plex": { "command": "./synapse-mcp-go" },
        "radarr": { "command": "./radarr-mcp" },
        "sonarr": { "command": "./sonarr-mcp" },
        "sabnzbd": { "command": "./sabnzbd-mcp" }
      }
    }
    
  3. 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 VariableDescriptionAllowed Values / Defaults
LLM_PROVIDERThe backend LLM provider to use.gemini (default), openai, ollama
LLM_MODELThe model name to run.Defaults: gemini-3.5-flash (Gemini), gpt-4o (OpenAI), llama3 (Ollama)
LLM_API_BASE_URLThe endpoint URL of the model provider.Defaults: Google API (Gemini), https://api.openai.com/v1 (OpenAI), http://localhost:11434/v1 (Ollama)
LLM_API_KEYThe 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-client library to interact with the Plex Media Server and Plex.tv APIs.