Skip to main content
Glama

SWAG MCP

PyPI ghcr.io

MCP server for managing SWAG reverse-proxy configuration files, backups, logs, and health checks. Uses a single swag action router backed by local filesystem or SSH-accessible SWAG config storage.

Overview

SWAG MCP generates and manages nginx subdomain proxy configurations for SWAG (Secure Web Application Gateway). Every generated config includes MCP-compatible security headers unconditionally, making configs suitable for both standard web services and MCP/AI services.

Related MCP server: Fleet

What this repository ships

  • swag_mcp/: server, config, middleware, models, services, tools, and templates

  • config/: local config and test assets

  • docs/: template notes, test commands, and design records

  • .claude-plugin/, .codex-plugin/, gemini-extension.json: client manifests

  • docker-compose.yaml, Dockerfile, entrypoint.sh: container deployment

MCP surface

Tool

Tool

Purpose

swag

Unified action router for config, logs, backups, and health checks

swag_help

Return help for the SWAG MCP server — lists all available actions and sub-actions

Actions

Action

Purpose

Required params

list

List config files

none

create

Create a new reverse-proxy config

config_name, server_name, upstream_app, upstream_port

view

View config contents

config_name

edit

Replace config contents

config_name, new_content

update

Update a specific field

config_name, update_field, update_value

remove

Remove a config

config_name

logs

Read SWAG logs

none

backups

List or clean up backup files

backup_action

health_check

Probe a service endpoint

domain

Resources

URI

Type

Description

swag://

Directory

All active .conf files (excluding .sample)

swag://configs/live

Stream

Real-time config change events

swag://health/stream

Stream

Real-time health status for monitored services

swag://logs/stream

Stream

Live nginx error log tail

Installation

Install as a Claude Code plugin. You will be prompted for:

  • SWAG Proxy Configs Path -- local path to proxy-confs directory

  • SWAG MCP Server URL -- base URL of the running HTTP server

  • SWAG MCP API Token -- bearer token for the HTTP server

The plugin connects to the server's native streamable-HTTP endpoint.

/plugin marketplace add jmagar/claude-homelab
/plugin install swag-mcp @jmagar-claude-homelab

The plugin uses native HTTP transport and appends /mcp to the configured server URL.

Docker Compose

cp .env.example .env
chmod 600 .env
# Edit .env with your paths and token
docker compose up -d

The container always runs on internal port 8000. Compose publishes it on 127.0.0.1:49152 by default; set SWAG_MCP_PORT to control the host port and SWAG_MCP_BIND_ADDRESS only when you intentionally expose it beyond loopback.

Local development

just setup       # copies .env.example and runs uv sync
just dev         # starts the server

Configuration

Two deployment paths are supported:

Path

Transport

Credentials

Auth

Plugin (HTTP)

http

userConfig in plugin settings

Bearer token

Docker (HTTP)

http

.env file

Bearer token

See docs/CONFIG.md for full variable reference. All variables use the SWAG_MCP_ prefix.

Core

Variable

Required

Default

Description

SWAG_MCP_PROXY_CONFS_PATH

no

/swag/nginx/proxy-confs

Local path to SWAG proxy confs directory

SWAG_MCP_PROXY_CONFS_URI

no

``

Overrides PROXY_CONFS_PATH when set. Accepts a local path or SSH URI.

SWAG_MCP_SWAG_LOG_BASE_PATH

no

/swag/log

Base path for SWAG log files (local or remote)

SWAG_MCP_TEMPLATE_PATH

no

templates

Path to Jinja2 templates directory

Server

Variable

Required

Default

Description

SWAG_MCP_HOST

no

127.0.0.1

Bind address for the MCP server

SWAG_MCP_BIND_ADDRESS

no

127.0.0.1

Docker host bind address for the published MCP port

SWAG_MCP_PORT

no

49152

Host-side port (Docker only; container always uses 8000)

SWAG_MCP_TOKEN

recommended

``

Bearer token enforced by FastMCP for direct MCP server access

SWAG_MCP_NO_AUTH

no

false

Set true only for loopback/proxy-isolated deployments without server auth

Defaults

Variable

Required

Default

Description

SWAG_MCP_DEFAULT_WEB_AUTH_METHOD

no

authelia

Web endpoint auth method used when create omits auth_method; this is not MCP server auth

SWAG_MCP_DEFAULT_QUIC_ENABLED

no

false

QUIC default for new configs

Backups

Variable

Required

Default

Description

SWAG_MCP_BACKUP_RETENTION_DAYS

no

30

Days to keep backup files. Cleanup runs at startup and on backups action=cleanup.

Logging

Variable

Required

Default

Description

SWAG_MCP_LOG_LEVEL

no

INFO

DEBUG / INFO / WARNING / ERROR / CRITICAL

SWAG_MCP_LOG_DIRECTORY

no

/app/.swag-mcp/logs

Directory for rotating log files

SWAG_MCP_LOG_FILE_ENABLED

no

true

Write logs to files in addition to stdout

SWAG_MCP_LOG_FILE_MAX_BYTES

no

10485760

Max log file size before rotation (10 MB)

SWAG_MCP_ENABLE_STRUCTURED_LOGGING

no

false

Emit JSON structured logs

SWAG_MCP_LOG_PAYLOADS

no

false

Include request/response bodies in logs

SWAG_MCP_LOG_PAYLOAD_MAX_LENGTH

no

1000

Max payload length when LOG_PAYLOADS=true

Performance

Variable

Required

Default

Description

SWAG_MCP_SLOW_OPERATION_THRESHOLD_MS

no

1000

Log a warning when any operation exceeds this threshold

SWAG_MCP_ENABLE_RETRY_MIDDLEWARE

no

true

Automatically retry failed operations

SWAG_MCP_MAX_RETRIES

no

3

Max retry attempts

Health checks

Variable

Required

Default

Description

SWAG_MCP_HEALTH_CHECK_INSECURE

no

false

Skip SSL verification for health probes. Not recommended for production.

Rate limiting

Variable

Required

Default

Description

SWAG_MCP_RATE_LIMIT_ENABLED

no

false

Enable the rate-limiting middleware

SWAG_MCP_RATE_LIMIT_RPS

no

10.0

Sustained requests per second

SWAG_MCP_RATE_LIMIT_BURST

no

20

Burst capacity above the RPS limit

Authentication methods

Pass auth_method to create to control which SWAG auth snippet the config includes. The default is set by SWAG_MCP_DEFAULT_WEB_AUTH_METHOD.

Method

When to use

none

Public services, or services behind an external SSO not managed by SWAG

basic

Simple HTTP basic auth. Credentials managed by SWAG.

ldap

LDAP/Active Directory authentication via SWAG's built-in LDAP support

authelia

Self-hosted SSO with 2FA. Most common for homelab services.

authentik

Self-hosted identity provider with rich policy engine

tinyauth

Lightweight single-user auth for internal tools

oauth

OAuth 2.1 gateway (mcp-oauth). Use for MCP/AI services that handle their own auth flow.

none exposes the service without authentication. Only use it if access is restricted at the network or proxy layer.

Template system

All configs are generated from a single Jinja2 template: templates/mcp.subdomain.conf.j2. Only subdomain-style configs are supported (service.domain.tld).

Template variables

Variable

Type

Required

Description

service_name

string

yes

Derived from config_name (e.g., jellyfin)

server_name

string

yes

Domain name (e.g., media.example.com)

upstream_app

string

yes

Container name or IP for the main service

upstream_port

integer

yes

Port the main service listens on

upstream_proto

string

yes

http or https

mcp_upstream_app

string

no

Container name or IP for the MCP endpoint. Defaults to upstream_app.

mcp_upstream_port

integer

no

Port for the MCP endpoint. Defaults to upstream_port.

mcp_upstream_proto

string

no

Protocol for the MCP endpoint. Defaults to upstream_proto.

auth_method

string

yes

One of the auth methods listed above

enable_quic

boolean

yes

Add listen 443 quic listeners and Alt-Svc header

MCP security headers

These headers are included unconditionally in every generated config, regardless of auth method:

Header

Value

Purpose

X-MCP-Version

2025-11-25

Advertises the MCP protocol version

Referrer-Policy

strict-origin-when-cross-origin

Limits referrer leakage

DNS rebinding protection is also applied unconditionally. The origin_valid variable is set to 1 only for empty origins, https://$server_name, localhost variants, and *.anthropic.com / *.claude.ai origins. Requests to /mcp with an invalid origin receive a 403 JSON error.

Split routing

Split routing sends the main application traffic to one upstream and MCP traffic to a different upstream. Use it when your MCP/AI service runs on a separate container or host (e.g., a GPU server).

Without split routing, both / and /mcp proxy to the same upstream_app:upstream_port.

With split routing:

  • / and /healthupstream_app:upstream_port

  • /mcp and /sessionmcp_upstream_app:mcp_upstream_port

Example: Jellyfin with a separate AI backend

action=create
config_name=jellyfin.subdomain.conf
server_name=jellyfin.example.com
upstream_app=jellyfin
upstream_port=8096
upstream_proto=http
mcp_upstream_app=ai-gpu-server
mcp_upstream_port=8080
mcp_upstream_proto=http
auth_method=authelia

This generates:

  • location /http://jellyfin:8096 (protected by Authelia)

  • location /mcphttp://ai-gpu-server:8080 (protected by OAuth verify)

  • location /healthhttp://jellyfin:8096 (no auth, for health probes)

If mcp_upstream_app is omitted, all three locations proxy to jellyfin:8096.

SSH / remote configuration

Set SWAG_MCP_PROXY_CONFS_URI to manage configs on a remote SWAG host over SSH.

Supported URI formats:

# Default key, default port 22
SWAG_MCP_PROXY_CONFS_URI=swag-server:/mnt/appdata/swag/nginx/proxy-confs

# Explicit user
SWAG_MCP_PROXY_CONFS_URI=admin@swag-server:/mnt/appdata/swag/nginx/proxy-confs

# Explicit user and port
SWAG_MCP_PROXY_CONFS_URI=admin@swag-server:2222:/mnt/appdata/swag/nginx/proxy-confs

Requirements:

  • SSH key-based (passwordless) access to the remote host

  • Mount an app-specific SSH directory with only the needed key, config, and known_hosts entries into the container (see SWAG_MCP_SSH_HOST_PATH in .env.example)

  • SWAG_MCP_SWAG_LOG_BASE_PATH should point to the log base path on the same remote host

When SWAG_MCP_PROXY_CONFS_URI is set it takes precedence over SWAG_MCP_PROXY_CONFS_PATH.

Usage examples

List

swag action=list
swag action=list list_filter=active
swag action=list list_filter=samples query=plex offset=0 limit=20

list_filter accepts all (default), active (.conf files only), or samples (.conf.sample files only).

Create

swag action=create
     config_name=jellyfin.subdomain.conf
     server_name=jellyfin.example.com
     upstream_app=jellyfin
     upstream_port=8096

After creation, a health check runs automatically against server_name.

View

swag action=view config_name=jellyfin.subdomain.conf

Edit

Replaces the full file content:

swag action=edit
     config_name=jellyfin.subdomain.conf
     new_content="..."
     create_backup=true

Update

Updates a single field in place:

swag action=update config_name=jellyfin.subdomain.conf update_field=port update_value=8097
swag action=update config_name=jellyfin.subdomain.conf update_field=upstream update_value=jellyfin-new
swag action=update config_name=jellyfin.subdomain.conf update_field=app update_value=jellyfin:8097
swag action=update config_name=jellyfin.subdomain.conf update_field=add_mcp update_value=/mcp

update_field options:

Value

update_value format

Effect

port

port number

Updates upstream_port

upstream

container name

Updates upstream_app

app

app:port

Updates both upstream_app and upstream_port

add_mcp

path (e.g., /mcp)

Injects an MCP location block into the config

After an update, a health check runs automatically.

Remove

swag action=remove config_name=jellyfin.subdomain.conf
swag action=remove config_name=jellyfin.subdomain.conf create_backup=false

Logs

swag action=logs log_type=nginx-error lines=100
swag action=logs log_type=nginx-access lines=50
swag action=logs log_type=fail2ban lines=200

Log types and format

log_type

Source file

Format

nginx-access

$SWAG_LOG_BASE_PATH/nginx/access.log

Combined nginx access log: IP, timestamp, method, path, status, bytes, referrer, user-agent

nginx-error

$SWAG_LOG_BASE_PATH/nginx/error.log

Nginx error format: timestamp, level, PID, message

fail2ban

$SWAG_LOG_BASE_PATH/fail2ban/fail2ban.log

fail2ban log: timestamp, level, jail name, action, IP

letsencrypt

$SWAG_LOG_BASE_PATH/letsencrypt/letsencrypt.log

Certbot output: certificate request, renewal, errors

renewal

$SWAG_LOG_BASE_PATH/letsencrypt/renewal.log

Periodic renewal cron output

lines accepts 1–1000 (default 50).

Backups

swag action=backups backup_action=list
swag action=backups backup_action=cleanup retention_days=7
swag action=backups backup_action=cleanup retention_days=0

backup_action=cleanup removes backup files older than retention_days. Pass retention_days=0 to use the server default (SWAG_MCP_BACKUP_RETENTION_DAYS).

Backup files are named <original>.backup.<timestamp>. They are created automatically before any edit, update, or remove operation when create_backup=true (the default).

Cleanup also runs at server startup. The startup cleanup uses SWAG_MCP_BACKUP_RETENTION_DAYS.

Health check

swag action=health_check domain=jellyfin.example.com
swag action=health_check domain=jellyfin.example.com timeout=10 follow_redirects=false

Response fields

Field

Type

Description

success

boolean

true only for 2xx responses, plus 406 on /mcp because that confirms the MCP endpoint exists but requires POST. Other statuses fail and are reported in endpoint_results.

domain

string

Domain that was checked

status_code

integer or null

HTTP status code returned

response_time_ms

integer or null

Round-trip time in milliseconds

error

string or null

Error message if the probe failed

endpoint_results

array

Per-endpoint probe attempts with URL, success flag, status, timing, and error detail

timeout accepts 1–300 seconds (default 30). The server adds a 10-second buffer on top of timeout for its own wait.

SSL certificate verification can be disabled globally with SWAG_MCP_HEALTH_CHECK_INSECURE=true.

Development commands

just setup        # copy .env.example and install dependencies
just dev          # start the server with uv
just lint         # ruff check
just fmt          # ruff format
just typecheck    # ty check
just test         # pytest
just build        # build a local swag-mcp image for manual testing
just up           # docker compose up -d
just down         # docker compose down
just logs         # docker compose logs -f
just health       # curl http://127.0.0.1:${SWAG_MCP_PORT:-49152}/health | jq
just gen-token    # generate a random bearer token

Operations

The Docker service publishes 127.0.0.1:49152 by default. Keep production MCP ports on high-numbered, documented ports and run just preflight before deployment to validate the external Docker network and selected host port.

Operational failure signals include Docker health becoming unhealthy, repeated tool error responses, timeout messages for create/edit/update/log operations, slow-operation warnings, health-check failures for known-good domains, and nginx error log spikes. Use docs/mcp/LOGS.md for signal triage and docs/mcp/DEPLOY.md for rollback and restore.

Backups are created before destructive edits, updates, and removals. To restore, list backups with swag(action="backups", backup_action="list"), copy the chosen backup content back with swag(action="edit", create_backup=true), validate nginx syntax, reload SWAG, and record the restore in docs/deployment-log.md.

Verification

just lint
just typecheck
just test

Check the server health endpoint:

just health
# {"status": "healthy", "service": "swag-mcp", "version": "1.1.6"}

The /health endpoint is also used by the Docker HEALTHCHECK directive. It always returns:

Field

Value

status

"healthy"

service

"swag-mcp"

version

Package version from importlib.metadata

Plugin

Category

Description

homelab-core

core

Core agents, commands, skills, and setup/health workflows for homelab management.

overseerr-mcp

media

Search movies and TV shows, submit requests, and monitor failed requests via Overseerr.

unraid-mcp

infrastructure

Query, monitor, and manage Unraid servers: Docker, VMs, array, parity, and live telemetry.

unifi-mcp

infrastructure

Monitor and manage UniFi devices, clients, firewall rules, and network health.

gotify-mcp

utilities

Send and manage push notifications via a self-hosted Gotify server.

synapse-mcp

infrastructure

Docker management (Flux) and SSH remote operations (Scout) across homelab hosts.

arcane-mcp

infrastructure

Manage Docker environments, containers, images, volumes, networks, and GitOps via Arcane.

syslog-mcp

infrastructure

Receive, index, and search syslog streams from all homelab hosts via SQLite FTS5.

plugin-lab

dev-tools

Scaffold, review, align, and deploy homelab MCP plugins with agents and canonical templates.

License

MIT

A
license - permissive license
-
quality - not tested
A
maintenance

Maintenance

Maintainers
Response time
Release cycle
1Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jmagar/swag-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server