AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP verified MIT Self-run

Swag Mcp

mcp-jmagar-swag-mcp · by jmagar

SWAG reverse proxy configuration management via MCP. Create, edit, view, and manage nginx proxy configurations with auth integration.

No reviews yet
0 installs
29 views
0.0% view→install

Install

$ agentstack add mcp-jmagar-swag-mcp

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-jmagar-swag-mcp)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Swag Mcp? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

SWAG MCP

[](https://pypi.org/project/swag-mcp/) [](https://github.com/jmagar/swag-mcp/pkgs/container/swag-mcp)

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.

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

Plugin (recommended)

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](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 PROXYCONFSPATH when set. Accepts a local path or SSH URI. | | SWAGMCPSWAGLOGBASEPATH | no | /swag/log | Base path for SWAG log files (local or remote) | | SWAGMCPTEMPLATEPATH | 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 | | SWAGMCPNO_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 .backup.. 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         # pyt

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [jmagar](https://github.com/jmagar)
- **Source:** [jmagar/swag-mcp](https://github.com/jmagar/swag-mcp)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.